PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 5.5
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v5.5
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / js / formidable_admin.js

formidable_admin.js in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 5.5, at js/formidable_admin.js

10,219 lines 307.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* exported frm_add_logic_row, frm_remove_tag, frm_show_div, frmCheckAll, frmCheckAllLevel */
2
3 var frmAdminBuild;
4
5 var FrmFormsConnect = window.FrmFormsConnect || ( function( document, window, $ ) {
6
7 /*global jQuery:false, frm_admin_js, frmGlobal, ajaxurl */
8
9 var el = {
10 licenseBox: document.getElementById( 'frm_license_top' ),
11 messageBox: document.getElementsByClassName( 'frm_pro_license_msg' )[0],
12 btn: document.getElementById( 'frm-settings-connect-btn' ),
13 reset: document.getElementById( 'frm_reconnect_link' )
14 };
15
16 /**
17 * Public functions and properties.
18 *
19 * @since 4.03
20 *
21 * @type {Object}
22 */
23 var app = {
24
25 /**
26 * Register connect button event.
27 *
28 * @since 4.03
29 */
30 init: function() {
31 $( document.getElementById( 'frm_deauthorize_link' ) ).on( 'click', app.deauthorize );
32 $( '.frm_authorize_link' ).on( 'click', app.authorize );
33 if ( el.reset !== null ) {
34 $( el.reset ).on( 'click', app.reauthorize );
35 }
36
37 $( el.btn ).on( 'click', function( e ) {
38 e.preventDefault();
39 app.gotoUpgradeUrl();
40 });
41
42 window.addEventListener( 'message', function( msg ) {
43 if ( msg.origin.replace( /\/$/, '' ) !== frmGlobal.app_url.replace( /\/$/, '' ) ) {
44 return;
45 }
46
47 if ( ! msg.data || 'object' !== typeof msg.data ) {
48 console.error( 'Messages from "' + frmGlobal.app_url + '" must contain an api key string.' );
49 return;
50 }
51
52 app.updateForm( msg.data );
53 });
54
55 jQuery( document ).on( 'mouseover', '#frm_new_form_modal .frm-selectable', function() {
56 var $item = jQuery( this ),
57 $icons = $item.find( '.frm-hover-icons' ),
58 $clone;
59
60 if ( ! $icons.length ) {
61 $clone = jQuery( '#frm-hover-icons-template' ).clone();
62 $clone.removeAttr( 'id' );
63 $item.append( $clone );
64 }
65
66 $icons.show();
67 });
68
69 jQuery( document ).on( 'mouseout', '#frm_new_form_modal .frm-selectable', function() {
70 var $item = jQuery( this ),
71 $icons = $item.find( '.frm-hover-icons' );
72
73 if ( $icons.length ) {
74 $icons.hide();
75 }
76 });
77 },
78
79 /**
80 * Go to upgrade url.
81 *
82 * @since 4.03
83 */
84 gotoUpgradeUrl: function() {
85 var w = window.open( frmGlobal.app_url + '/api-connect/', '_blank', 'location=no,width=500,height=730,scrollbars=0' );
86 w.focus();
87 },
88
89 updateForm: function( response ) {
90
91 // Start spinner.
92 var btn = el.btn;
93 btn.classList.add( 'frm_loading_button' );
94
95 if ( response.url !== '' ) {
96 app.showProgress({
97 success: true,
98 message: 'Installing...'
99 });
100 var fallback = setTimeout( function() {
101 app.showProgress({
102 success: true,
103 message: 'Installing is taking longer than expected. <a class="frm-install-addon button button-primary frm-button-primary" rel="' + response.url + '" aria-label="Install">Install Now</a>'
104 });
105 }, 10000 );
106 $.ajax({
107 type: 'POST',
108 url: ajaxurl,
109 dataType: 'json',
110 data: {
111 action: 'frm_connect',
112 plugin: response.url,
113 nonce: frmGlobal.nonce
114 },
115 success: function() {
116 clearTimeout( fallback );
117 app.activateKey( response );
118 },
119 error: function( xhr, textStatus, e ) {
120 clearTimeout( fallback );
121 btn.classList.remove( 'frm_loading_button' );
122 app.showMessage({
123 success: false,
124 message: e
125 });
126 }
127 });
128 } else if ( response.key !== '' ) {
129 app.activateKey( response );
130 }
131 },
132
133 activateKey: function( response ) {
134 var btn = el.btn;
135 if ( response.key === '' ) {
136 btn.classList.remove( 'frm_loading_button' );
137 } else {
138 app.showProgress({
139 success: true,
140 message: 'Activating...'
141 });
142 $.ajax({
143 type: 'POST',
144 url: ajaxurl,
145 dataType: 'json',
146 data: {
147 action: 'frm_addon_activate',
148 license: response.key,
149 plugin: 'formidable_pro',
150 wpmu: 0,
151 nonce: frmGlobal.nonce
152 },
153 success: function( msg ) {
154 btn.classList.remove( 'frm_loading_button' );
155
156 if ( msg.success === true ) {
157 el.licenseBox.classList.replace( 'frm_unauthorized_box', 'frm_authorized_box' );
158 }
159
160 app.showMessage( msg );
161 },
162 error: function( xhr, textStatus, e ) {
163 btn.classList.remove( 'frm_loading_button' );
164 app.showMessage({
165 success: false,
166 message: e
167 });
168 }
169 });
170 }
171 },
172
173 /* Manual license authorization */
174 authorize: function() {
175 /*jshint validthis:true */
176 var button = this;
177 var pluginSlug = this.getAttribute( 'data-plugin' );
178 var input = document.getElementById( 'edd_' + pluginSlug + '_license_key' );
179 var license = input.value;
180 var wpmu = document.getElementById( 'proplug-wpmu' );
181 this.classList.add( 'frm_loading_button' );
182 if ( wpmu === null ) {
183 wpmu = 0;
184 } else if ( wpmu.checked ) {
185 wpmu = 1;
186 } else {
187 wpmu = 0;
188 }
189
190 $.ajax({
191 type: 'POST', url: ajaxurl, dataType: 'json',
192 data: {
193 action: 'frm_addon_activate',
194 license: license,
195 plugin: pluginSlug,
196 wpmu: wpmu,
197 nonce: frmGlobal.nonce
198 },
199 success: function( msg ) {
200 app.afterAuthorize( msg, input );
201 button.classList.remove( 'frm_loading_button' );
202 }
203 });
204 },
205
206 afterAuthorize: function( msg, input ) {
207 if ( msg.success === true ) {
208 input.value = '•••••••••••••••••••';
209 }
210
211 app.showMessage( msg );
212 },
213
214 showProgress: function( msg ) {
215 var messageBox = el.messageBox;
216 if ( msg.success === true ) {
217 messageBox.classList.remove( 'frm_error_style' );
218 messageBox.classList.add( 'frm_message', 'frm_updated_message' );
219 } else {
220 messageBox.classList.add( 'frm_error_style' );
221 messageBox.classList.remove( 'frm_message', 'frm_updated_message' );
222 }
223 messageBox.classList.remove( 'frm_hidden' );
224 messageBox.innerHTML = msg.message;
225 },
226
227 showMessage: function( msg ) {
228 var messageBox = el.messageBox;
229
230 if ( msg.success === true ) {
231 var d = el.licenseBox;
232 d.className = d.className.replace( 'frm_unauthorized_box', 'frm_authorized_box' );
233 messageBox.classList.remove( 'frm_error_style' );
234 messageBox.classList.add( 'frm_message', 'frm_updated_message' );
235 } else {
236 messageBox.classList.add( 'frm_error_style' );
237 messageBox.classList.remove( 'frm_message', 'frm_updated_message' );
238 }
239
240 messageBox.classList.remove( 'frm_hidden' );
241 messageBox.innerHTML = msg.message;
242 if ( msg.message !== '' ) {
243 setTimeout( function() {
244 messageBox.innerHTML = '';
245 messageBox.classList.add( 'frm_hidden' );
246 messageBox.classList.remove( 'frm_error_style', 'frm_message', 'frm_updated_message' );
247 }, 10000 );
248 var refreshPage = document.querySelectorAll( '#frm-welcome' );
249 if ( refreshPage.length > 0 ) {
250 window.location.reload();
251 }
252 }
253 },
254
255 /* Clear the site license cache */
256 reauthorize: function() {
257 /*jshint validthis:true */
258 this.innerHTML = '<span class="frm-wait frm_spinner" style="visibility:visible;float:none"></span>';
259
260 $.ajax({
261 type: 'POST',
262 url: ajaxurl,
263 dataType: 'json',
264 data: {
265 action: 'frm_reset_cache',
266 plugin: 'formidable_pro',
267 nonce: frmGlobal.nonce
268 },
269 success: function( msg ) {
270 el.reset.innerHTML = msg.message;
271 if ( el.reset.getAttribute( 'data-refresh' ) === '1' ) {
272 window.location.reload();
273 }
274 }
275 });
276 return false;
277 },
278
279 deauthorize: function() {
280 /*jshint validthis:true */
281 if ( ! confirm( frmGlobal.deauthorize ) ) {
282 return false;
283 }
284 var pluginSlug = this.getAttribute( 'data-plugin' ),
285 input = document.getElementById( 'edd_' + pluginSlug + '_license_key' ),
286 license = input.value,
287 link = this;
288
289 this.innerHTML = '<span class="frm-wait frm_spinner" style="visibility:visible;"></span>';
290
291 $.ajax({
292 type: 'POST',
293 url: ajaxurl,
294 data: {
295 action: 'frm_addon_deactivate',
296 license: license,
297 plugin: pluginSlug,
298 nonce: frmGlobal.nonce
299 },
300 success: function() {
301 el.licenseBox.className = el.licenseBox.className.replace( 'frm_authorized_box', 'frm_unauthorized_box' );
302 input.value = '';
303 link.innerHTML = '';
304 }
305 });
306 return false;
307 }
308 };
309
310 // Provide access to public functions/properties.
311 return app;
312
313 }( document, window, jQuery ) );
314
315 function frmAdminBuildJS() {
316 //'use strict';
317
318 /*global jQuery:false, frm_admin_js, frmGlobal, ajaxurl, fromDom */
319
320 const { tag, div, span, a, svg, img } = frmDom;
321 const { doJsonFetch } = frmDom.ajax;
322 const icons = {
323 save: svg({ href: '#frm_save_icon' }),
324 drag: svg({ href: '#frm_drag_icon', classList: [ 'frm_drag_icon', 'frm-drag' ] })
325 };
326
327 var $newFields = jQuery( document.getElementById( 'frm-show-fields' ) ),
328 builderForm = document.getElementById( 'new_fields' ),
329 thisForm = document.getElementById( 'form_id' ),
330 cancelSort = false,
331 copyHelper = false,
332 fieldsUpdated = 0,
333 thisFormId = 0,
334 autoId = 0,
335 optionMap = {},
336 lastNewActionIdReturned = 0,
337 __ = wp.i18n.__;
338
339 if ( thisForm !== null ) {
340 thisFormId = thisForm.value;
341 }
342
343 // Global settings
344 var s;
345
346 function showElement( element ) {
347 element[0].style.display = '';
348 }
349
350 function hideElement( element ) {
351 element[0].style.display = 'none';
352 }
353
354 function empty( $obj ) {
355 if ( $obj !== null ) {
356 while ( $obj.firstChild ) {
357 $obj.removeChild( $obj.firstChild );
358 }
359 }
360 }
361
362 function addClass( $obj, className ) {
363 if ( $obj.classList ) {
364 $obj.classList.add( className );
365 } else {
366 $obj.className += ' ' + className;
367 }
368 }
369
370 function confirmClick( e ) {
371 /*jshint validthis:true */
372 e.stopPropagation();
373 e.preventDefault();
374 confirmLinkClick( this );
375 }
376
377 function confirmLinkClick( link ) {
378 var message = link.getAttribute( 'data-frmverify' );
379
380 if ( message === null || link.id === 'frm-confirmed-click' ) {
381 return true;
382 } else {
383 return confirmModal( link );
384 }
385 }
386
387 function confirmModal( link ) {
388 var caution, verify, $confirmMessage, frmCaution, i, dataAtts,
389 $info = initModal( '#frm_confirm_modal', '400px' ),
390 continueButton = document.getElementById( 'frm-confirmed-click' );
391
392 if ( $info === false ) {
393 return false;
394 }
395
396 caution = link.getAttribute( 'data-frmcaution' );
397 verify = link.getAttribute( 'data-frmverify' );
398 $confirmMessage = jQuery( '.frm-confirm-msg' );
399 $confirmMessage.empty();
400
401 if ( caution ) {
402 frmCaution = document.createElement( 'div' );
403 frmCaution.classList.add( 'frm-caution' );
404 frmCaution.appendChild( document.createTextNode( caution ) );
405 $confirmMessage.append( frmCaution );
406 }
407
408 if ( verify ) {
409 $confirmMessage.append( document.createTextNode( verify ) );
410 }
411
412 removeAtts = continueButton.dataset;
413 for ( i in dataAtts ) {
414 continueButton.removeAttribute( 'data-' + i );
415 }
416
417 dataAtts = link.dataset;
418 for ( i in dataAtts ) {
419 if ( i !== 'frmverify' ) {
420 continueButton.setAttribute( 'data-' + i, dataAtts[i]);
421 }
422 }
423
424 $info.dialog( 'open' );
425 continueButton.setAttribute( 'href', link.getAttribute( 'href' ) );
426 return false;
427 }
428
429 function infoModal( msg ) {
430 var $info = initModal( '#frm_info_modal', '400px' );
431
432 if ( $info === false ) {
433 return false;
434 }
435
436 jQuery( '.frm-info-msg' ).html( msg );
437
438 $info.dialog( 'open' );
439 return false;
440 }
441
442 function toggleItem( e ) {
443 /*jshint validthis:true */
444 var toggle = this.getAttribute( 'data-frmtoggle' ),
445 text = this.getAttribute( 'data-toggletext' ),
446 items = jQuery( toggle );
447
448 e.preventDefault();
449
450 if ( items.is( ':visible' ) ) {
451 items.show();
452 } else {
453 items.hide();
454 }
455
456 if ( text !== null && text !== '' ) {
457 this.setAttribute( 'data-toggletext', this.innerHTML );
458 this.innerHTML = text;
459 }
460
461 return false;
462 }
463
464 function hideShowItem( e ) {
465 /*jshint validthis:true */
466 var hide = this.getAttribute( 'data-frmhide' ),
467 show = this.getAttribute( 'data-frmshow' ),
468 toggleClass = this.getAttribute( 'data-toggleclass' );
469
470 e.preventDefault();
471 if ( toggleClass === null ) {
472 toggleClass = 'frm_hidden';
473 }
474
475 if ( hide !== null ) {
476 jQuery( hide ).addClass( toggleClass );
477 }
478
479 if ( show !== null ) {
480 jQuery( show ).removeClass( toggleClass );
481 }
482
483 var current = this.parentNode.querySelectorAll( 'a.current' );
484 if ( current !== null ) {
485 for ( var i = 0; i < current.length; i++ ) {
486 current[ i ].classList.remove( 'current' );
487 }
488 this.classList.add( 'current' );
489 }
490
491 return false;
492 }
493
494 function setupMenuOffset() {
495 window.onscroll = document.documentElement.onscroll = setMenuOffset;
496 setMenuOffset();
497 }
498
499 function setMenuOffset() {
500 var fields = document.getElementById( 'frm_adv_info' );
501 if ( fields === null ) {
502 return;
503 }
504
505 var currentOffset = document.documentElement.scrollTop || document.body.scrollTop; // body for Safari
506 if ( currentOffset === 0 ) {
507 fields.classList.remove( 'frm_fixed' );
508 return;
509 }
510
511 var posEle = document.getElementById( 'frm_position_ele' );
512 if ( posEle === null ) {
513 return;
514 }
515
516 var eleOffset = jQuery( posEle ).offset();
517 var offset = eleOffset.top;
518 var desiredOffset = offset - currentOffset;
519 var menuHeight = 0;
520
521 var menu = document.getElementById( 'wpadminbar' );
522 if ( menu !== null ) {
523 menuHeight = menu.offsetHeight;
524 }
525
526 if ( desiredOffset < menuHeight ) {
527 desiredOffset = menuHeight;
528 }
529
530 if ( desiredOffset > menuHeight ) {
531 fields.classList.remove( 'frm_fixed' );
532 } else {
533 fields.classList.add( 'frm_fixed' );
534 if ( desiredOffset !== 32 ) {
535 fields.style.top = desiredOffset + 'px';
536 }
537 }
538 }
539
540 function loadTooltips() {
541 var wrapClass = jQuery( '.wrap, .frm_wrap' ),
542 confirmModal = document.getElementById( 'frm_confirm_modal' ),
543 doAction = false,
544 confirmedBulkDelete = false;
545
546 jQuery( confirmModal ).on( 'click', '[data-deletefield]', deleteFieldConfirmed );
547 jQuery( confirmModal ).on( 'click', '[data-removeid]', removeThisTag );
548 jQuery( confirmModal ).on( 'click', '[data-trashtemplate]', trashTemplate );
549
550 wrapClass.on( 'click', '.frm_remove_tag, .frm_remove_form_action', removeThisTag );
551 wrapClass.on( 'click', 'a[data-frmverify]', confirmClick );
552 wrapClass.on( 'click', 'a[data-frmtoggle]', toggleItem );
553 wrapClass.on( 'click', 'a[data-frmhide], a[data-frmshow]', hideShowItem );
554 wrapClass.on( 'click', '.widget-top,a.widget-action', clickWidget );
555
556 wrapClass.on( 'mouseenter.frm', '.frm_bstooltip, .frm_help', function() {
557 jQuery( this ).off( 'mouseenter.frm' );
558
559 jQuery( '.frm_bstooltip, .frm_help' ).tooltip( );
560 jQuery( this ).tooltip( 'show' );
561 });
562
563 jQuery( '.frm_bstooltip, .frm_help' ).tooltip( );
564
565 jQuery( document ).on( 'click', '#doaction, #doaction2', function( event ) {
566 var isTop = this.id === 'doaction',
567 suffix = isTop ? 'top' : 'bottom',
568 bulkActionSelector = document.getElementById( 'bulk-action-selector-' + suffix ),
569 confirmBulkDelete = document.getElementById( 'confirm-bulk-delete-' + suffix );
570
571 if ( bulkActionSelector !== null && confirmBulkDelete !== null ) {
572 doAction = this;
573
574 if ( ! confirmedBulkDelete && bulkActionSelector.value === 'bulk_delete' ) {
575 event.preventDefault();
576 confirmLinkClick( confirmBulkDelete );
577 return false;
578 }
579 } else {
580 doAction = false;
581 }
582 });
583
584 jQuery( document ).on( 'click', '#frm-confirmed-click', function( event ) {
585 if ( doAction === false ) {
586 return;
587 }
588
589 if ( this.getAttribute( 'href' ) === 'confirm-bulk-delete' ) {
590 event.preventDefault();
591 confirmedBulkDelete = true;
592 doAction.click();
593 return false;
594 }
595 });
596 }
597
598 function deleteTooltips() {
599 document.querySelectorAll( '.tooltip' ).forEach(
600 function( tooltip ) {
601 tooltip.remove();
602 }
603 );
604 }
605
606 function removeThisTag() {
607 /*jshint validthis:true */
608 let show, hide, removeMore;
609
610 if ( parseInt( this.getAttribute( 'data-skip-frm-js' ) ) || confirmLinkClick( this ) === false ) {
611 return;
612 }
613
614 const deleteButton = jQuery( this );
615 const id = deleteButton.attr( 'data-removeid' );
616
617 show = deleteButton.attr( 'data-showlast' );
618 if ( typeof show === 'undefined' ) {
619 show = '';
620 }
621
622 hide = deleteButton.attr( 'data-hidelast' );
623 if ( typeof hide === 'undefined' ) {
624 hide = '';
625 }
626
627 removeMore = deleteButton.attr( 'data-removemore' );
628
629 if ( show !== '' ) {
630 if ( deleteButton.closest( '.frm_add_remove' ).find( '.frm_remove_tag:visible' ).length > 1 ) {
631 show = '';
632 hide = '';
633 }
634 } else if ( id.indexOf( 'frm_postmeta_' ) === 0 ) {
635 if ( jQuery( '#frm_postmeta_rows .frm_postmeta_row' ).length < 2 ) {
636 show = '.frm_add_postmeta_row.button';
637 }
638 if ( jQuery( '.frm_toggle_cf_opts' ).length && jQuery( '#frm_postmeta_rows .frm_postmeta_row:not(#' + id + ')' ).last().length ) {
639 if ( show !== '' ) {
640 show += ',';
641 }
642 show += '#' + jQuery( '#frm_postmeta_rows .frm_postmeta_row:not(#' + id + ')' ).last().attr( 'id' ) + ' .frm_toggle_cf_opts';
643 }
644 }
645
646 const $fadeEle = jQuery( document.getElementById( id ) );
647 $fadeEle.fadeOut( 400, function() {
648 $fadeEle.remove();
649 fieldUpdated();
650
651 if ( hide !== '' ) {
652 jQuery( hide ).hide();
653 }
654
655 if ( show !== '' ) {
656 jQuery( show + ' a,' + show ).removeClass( 'frm_hidden' ).fadeIn( 'slow' );
657 }
658
659 if ( this.closest( '.frm_form_action_settings' ) ) {
660 const type = this.closest( '.frm_form_action_settings' ).querySelector( '.frm_action_name' ).value;
661 afterActionRemoved( type );
662 }
663 });
664
665 if ( typeof removeMore !== 'undefined' ) {
666 removeMore = jQuery( removeMore );
667 removeMore.fadeOut( 400, function() {
668 removeMore.remove();
669 });
670 }
671
672 if ( show !== '' ) {
673 jQuery( this ).closest( '.frm_logic_rows' ).fadeOut( 'slow' );
674 }
675
676 return false;
677 }
678
679 function afterActionRemoved( type ) {
680 checkActiveAction( type );
681
682 const hookName = 'frm_after_action_removed';
683 const hookArgs = { type };
684 wp.hooks.doAction( hookName, hookArgs );
685 }
686
687 function clickWidget( event, b ) {
688 /*jshint validthis:true */
689 var target = event.target;
690 if ( typeof b === 'undefined' ) {
691 b = this;
692 }
693
694 popCalcFields( b, false );
695
696 var cont = jQuery( b ).closest( '.frm_form_action_settings' );
697 if ( cont.length && typeof target !== 'undefined' ) {
698 var className = target.parentElement.className;
699 if ( 'string' === typeof className ) {
700 if ( className.indexOf( 'frm_email_icons' ) > -1 || className.indexOf( 'frm_toggle' ) > -1 ) {
701 // clicking on delete icon shouldn't open it
702 event.stopPropagation();
703 return;
704 }
705 }
706 }
707
708 var inside = cont.children( '.widget-inside' );
709
710 if ( cont.length && inside.find( 'p, div, table' ).length < 1 ) {
711 var actionId = cont.find( 'input[name$="[ID]"]' ).val();
712 var actionType = cont.find( 'input[name$="[post_excerpt]"]' ).val();
713 if ( actionType ) {
714 inside.html( '<span class="frm-wait frm_spinner"></span>' );
715 cont.find( '.spinner' ).fadeIn( 'slow' );
716 jQuery.ajax({
717 type: 'POST',
718 url: ajaxurl,
719 data: {
720 action: 'frm_form_action_fill',
721 action_id: actionId,
722 action_type: actionType,
723 nonce: frmGlobal.nonce
724 },
725 success: function( html ) {
726 inside.html( html );
727 initiateMultiselect();
728 showInputIcon( '#' + cont.attr( 'id' ) );
729 frmDom.autocomplete.initAutocomplete( 'page', inside );
730 jQuery( b ).trigger( 'frm-action-loaded' );
731 }
732 });
733 }
734 }
735
736 jQuery( b ).closest( '.frm_field_box' ).siblings().find( '.widget-inside' ).slideUp( 'fast' );
737 if ( ( typeof b.className !== 'undefined' && b.className.indexOf( 'widget-action' ) !== -1 ) || jQuery( b ).closest( '.start_divider' ).length < 1 ) {
738 return;
739 }
740
741 inside = jQuery( b ).closest( 'div.widget' ).children( '.widget-inside' );
742 if ( inside.is( ':hidden' ) ) {
743 inside.slideDown( 'fast' );
744 } else {
745 inside.slideUp( 'fast' );
746 }
747 }
748
749 function clickNewTab() {
750 /*jshint validthis:true */
751 var t = this.getAttribute( 'href' ),
752 c = t.replace( '#', '.' ),
753 $link = jQuery( this );
754
755 if ( typeof t === 'undefined' ) {
756 return false;
757 }
758
759 $link.closest( 'li' ).addClass( 'frm-tabs active' ).siblings( 'li' ).removeClass( 'frm-tabs active starttab' );
760 $link.closest( 'div' ).children( '.tabs-panel' ).not( t ).not( c ).hide();
761 document.getElementById( t.replace( '#', '' ) ).style.display = 'block';
762
763 if ( this.id === 'frm_insert_fields_tab' ) {
764 clearSettingsBox();
765 }
766 return false;
767 }
768
769 function clickTab( link, auto ) {
770 link = jQuery( link );
771 var t = link.attr( 'href' );
772 if ( typeof t === 'undefined' ) {
773 return;
774 }
775
776 var c = t.replace( '#', '.' );
777
778 link.closest( 'li' ).addClass( 'frm-tabs active' ).siblings( 'li' ).removeClass( 'frm-tabs active starttab' );
779 if ( link.closest( 'div' ).find( '.tabs-panel' ).length ) {
780 link.closest( 'div' ).children( '.tabs-panel' ).not( t ).not( c ).hide();
781 } else {
782 if ( document.getElementById( 'form_global_settings' ) !== null ) {
783 /* global settings */
784 var ajax = link.data( 'frmajax' );
785 link.closest( '.frm_wrap' ).find( '.tabs-panel, .hide_with_tabs' ).hide();
786 if ( typeof ajax !== 'undefined' && ajax == '1' ) {
787 loadSettingsTab( t );
788 }
789 } else {
790 /* form settings page */
791 jQuery( '#frm-categorydiv .tabs-panel, .hide_with_tabs' ).hide();
792 }
793 }
794 jQuery( t ).show();
795 jQuery( c ).show();
796
797 hideShortcodes();
798
799 if ( auto !== 'auto' ) {
800 // Hide success message on tab change.
801 jQuery( '.frm_updated_message' ).hide();
802 jQuery( '.frm_warning_style' ).hide();
803 }
804
805 if ( jQuery( link ).closest( '#frm_adv_info' ).length ) {
806 return;
807 }
808
809 if ( jQuery( '.frm_form_settings' ).length ) {
810 jQuery( '.frm_form_settings' ).attr( 'action', '?page=formidable&frm_action=settings&id=' + jQuery( '.frm_form_settings input[name="id"]' ).val() + '&t=' + t.replace( '#', '' ) );
811 } else {
812 jQuery( '.frm_settings_form' ).attr( 'action', '?page=formidable-settings&t=' + t.replace( '#', '' ) );
813 }
814 }
815
816 /* Form Builder */
817 function setupSortable( sort ) {
818 var startSort, container, $previousFieldContainer, opts;
819
820 startSort = false;
821 container = jQuery( '#post-body-content' );
822
823 opts = {
824 connectWith: 'ul.frm_sorting',
825 items: 'li.frm_field_box',
826 placeholder: 'sortable-placeholder',
827 axis: 'y',
828 cancel: '.widget,.frm_field_opts_list,input,textarea,select,.edit_field_type_end_divider,.frm_sortable_field_opts,.frm_noallow',
829 accepts: 'field_type_list',
830 forcePlaceholderSize: false,
831 tolerance: 'pointer',
832 handle: '.frm-move',
833 over: function() {
834 this.classList.add( 'drop-me' );
835 },
836 out: function() {
837 this.classList.remove( 'drop-me' );
838 },
839 receive: function( event, ui ) {
840 // Receive event occurs when an item in one sortable list is dragged into another sortable list
841
842 if ( cancelSort ) {
843 ui.item.addClass( 'frm_cancel_sort' );
844 return;
845 }
846
847 if ( typeof ui.item.attr( 'id' ) !== 'undefined' ) {
848 if ( ui.item.attr( 'id' ).indexOf( 'frm_field_id' ) > -1 ) {
849 // An existing field was dragged and dropped into, out of, or between sections
850 updateFieldAfterMovingBetweenSections( ui.item );
851 } else {
852 // A new field was dragged into the form
853 insertNewFieldByDragging( this, ui.item, opts );
854 }
855 } else if ( ui.item.hasClass( 'frm_field_box' ) ) {
856 // dragging a group.
857 getFieldsInRow( ui.item.children( 'ul' ) ).each(
858 function() {
859 updateFieldAfterMovingBetweenSections( jQuery( this ) );
860 }
861 );
862 }
863 },
864 change: function( event, ui ) {
865 // don't allow some field types inside section
866 if ( allowDrop( ui ) ) {
867 ui.placeholder.addClass( 'sortable-placeholder' ).removeClass( 'no-drop-placeholder' );
868 cancelSort = false;
869 } else {
870 ui.placeholder.addClass( 'no-drop-placeholder' ).removeClass( 'sortable-placeholder' );
871 cancelSort = true;
872 }
873 },
874 start: function( event, ui ) {
875 unselectFieldGroups();
876 deleteEmptyDividerWrappers();
877 maybeRemoveGroupHoverTarget();
878 closeOpenFieldDropdowns();
879 container.get( 0 ).classList.add( 'frm-dragging-field' );
880 if ( ui.item[0].offsetHeight > 120 ) {
881 jQuery( sort ).sortable( 'refreshPositions' );
882 }
883 if ( ui.item[0].classList.contains( 'frm-page-collapsed' ) ) {
884 // If a page if collapsed, expand it before dragging since only the page break will move.
885 toggleCollapsePage( jQuery( ui.item[0]) );
886 }
887 $previousFieldContainer = ui.item.closest( 'ul.frm_sorting' );
888 },
889 helper: function( e, li ) {
890 copyHelper = li.clone().insertAfter( li );
891 return li.clone().addClass( 'frm-sortable-helper' );
892 },
893 beforeStop: function( event, ui ) {
894 // If this was dropped at the beginning of a collpased page, open it.
895 var previous = ui.item[0].previousElementSibling;
896 if ( previous !== null && previous.classList.contains( 'frm-page-collapsed' ) ) {
897 toggleCollapsePage( jQuery( previous ) );
898 }
899 },
900 stop: function( event, ui ) {
901 var moving, $previousContainerFields, $closestFieldBox;
902
903 container.get( 0 ).classList.remove( 'frm-dragging-field' );
904 moving = jQuery( this );
905 copyHelper && copyHelper.remove();
906
907 if ( cancelSort ) {
908 moving.sortable( 'cancel' );
909 syncAfterDragAndDrop();
910 return;
911 }
912
913 $previousContainerFields = $previousFieldContainer.length ? getFieldsInRow( $previousFieldContainer ) : [];
914 if ( $previousFieldContainer.length ) {
915 if ( ! $previousContainerFields.length ) {
916 $closestFieldBox = $previousFieldContainer.closest( 'li.frm_field_box' );
917 if ( ! $closestFieldBox.hasClass( 'edit_field_type_divider' ) ) {
918 // remove an empty field group, but don't remove an empty section.
919 $closestFieldBox.remove();
920 }
921 } else {
922 syncLayoutClasses( $previousContainerFields.first() );
923 }
924 }
925
926 if ( ( 'frm-show-fields' === ui.item.parent().attr( 'id' ) || ui.item.parent().hasClass( 'start_divider' ) ) && ui.item.hasClass( 'form-field' ) ) {
927 // dragging an item into a new row.
928 wrapFieldLiInPlace( ui.item );
929 if ( $previousContainerFields.length ) {
930 // only if the previous container had other sibling fields, remove the previous layout class.
931 syncLayoutClasses( ui.item );
932 }
933 } else {
934 syncLayoutClasses( ui.item );
935 }
936
937 syncAfterDragAndDrop();
938 },
939 sort: function( event, ui ) {
940 var $row, $children, $lastChild, currentIndex, left;
941
942 container.scrollTop( function( i, v ) {
943 var moved, h, relativePos, y;
944
945 if ( startSort === false ) {
946 startSort = event.clientY;
947 return v;
948 }
949
950 moved = event.clientY - startSort;
951 h = this.offsetHeight;
952 relativePos = event.clientY - this.offsetTop;
953 y = relativePos - h / 2;
954 if ( relativePos > ( h - 50 ) && moved > 5 ) {
955 // scrolling down
956 return v + y * 0.1;
957 } else if ( relativePos < 50 && moved < -5 ) {
958 //scrolling up
959 return v - Math.abs( y * 0.1 );
960 }
961 });
962
963 maybeFixPlaceholderParent( ui, event );
964
965 $row = ui.placeholder.parent();
966 $children = getFieldsInRow( $row );
967 currentIndex = determineIndexBasedOffOfMousePositionInRow( $row, event.clientX );
968
969 if ( ! $children.length ) {
970 return;
971 }
972
973 if ( currentIndex === $children.length ) {
974 $lastChild = jQuery( $children.get( currentIndex - 1 ) );
975 left = $lastChild.offset().left - ui.placeholder.parent().offset().left + $lastChild.outerWidth();
976 $row.append( ui.placeholder );
977 } else {
978 left = jQuery( $children.get( currentIndex ) ).offset().left - $row.offset().left;
979 jQuery( $children.get( currentIndex ) ).before( ui.placeholder );
980 }
981
982 ui.placeholder.get( 0 ).style.left = left + 'px';
983 }
984 };
985
986 jQuery( sort ).sortable( opts );
987
988 setupFieldOptionSorting( jQuery( '#frm_builder_page' ) );
989 }
990
991 function syncAfterDragAndDrop() {
992 maybeRemoveNewCancelledFields();
993 maybeUncancelFields();
994 maybeFixEndDividers();
995 fixUnwrappedListItems();
996 toggleSectionHolder();
997 updateFieldOrder();
998
999 const event = new Event( 'frm_sync_after_drag_and_drop', { bubbles: false });
1000 document.dispatchEvent( event );
1001 }
1002
1003 function maybeRemoveNewCancelledFields() {
1004 Array.from( document.getElementById( 'frm-show-fields' ).children ).forEach(
1005 function( fieldBox ) {
1006 if ( fieldBox.classList.contains( 'frmbutton' ) && fieldBox.classList.contains( 'ui-draggable' ) ) {
1007 fieldBox.remove();
1008 }
1009 }
1010 );
1011 }
1012
1013 function maybeUncancelFields() {
1014 document.querySelectorAll( '.frm_cancel_sort' ).forEach(
1015 function( field ) {
1016 field.classList.remove( 'frm_cancel_sort' );
1017 }
1018 );
1019 }
1020
1021 /**
1022 * Make sure the end dividers are always a child of a start divider, at the bottom at the list.
1023 */
1024 function maybeFixEndDividers() {
1025 var endDividers = document.querySelectorAll( '.edit_field_type_end_divider' );
1026 if ( ! endDividers.length ) {
1027 return;
1028 }
1029 endDividers.forEach(
1030 function( endDivider ) {
1031 if ( endDivider.parentNode.classList.contains( 'start_divider' ) ) {
1032 // avoid having to call closest, but still append it as it might not be the last child.
1033 endDivider.parentNode.appendChild( endDivider );
1034 return;
1035 }
1036 endDivider.closest( '.start_divider' ).appendChild( endDivider );
1037 }
1038 );
1039 }
1040
1041 /**
1042 * Sortable struggles to put the field into the proper section if there are multiple in a field group. This helps fix some of those issues.
1043 */
1044 function maybeFixPlaceholderParent( ui, event ) {
1045 var elementFromPoint, wrapper, shouldAppend;
1046 elementFromPoint = document.elementFromPoint( event.clientX, event.clientY );
1047 if ( null === elementFromPoint ) {
1048 return;
1049 }
1050 wrapper = elementFromPoint.closest( '.frm_sorting' );
1051 if ( null === wrapper ) {
1052 return;
1053 }
1054 if ( ui.placeholder.closest( '.start_divider' ).parent().parent().find( '.start_divider' ).length < 2 ) {
1055 // placeholder is not in a problematic position that needs to be fixed so leave it.
1056 return;
1057 }
1058 shouldAppend = false;
1059 if ( wrapper.classList.contains( 'start_divider' ) ) {
1060 shouldAppend = jQuery( wrapper ).parent().parent().find( '.start_divider' ).length >= 2;
1061 } else if ( null !== wrapper.closest( '.start_divider' ) ) {
1062 shouldAppend = jQuery( wrapper ).closest( '.start_divider' ).parent().parent().find( '.start_divider' ).length >= 2;
1063 }
1064 if ( null !== wrapper.closest( '.frm-sortable-helper' ) ) {
1065 // avoid ever appending to the sortable helper.
1066 return;
1067 }
1068 if ( shouldAppend ) {
1069 // TODO instead of appendTo, we might need to look for the closest item, and appear above/below it.
1070 ui.placeholder.appendTo( wrapper );
1071 }
1072 }
1073
1074 function fixUnwrappedListItems() {
1075 document.querySelectorAll( 'ul.start_divider > li.form-field:not(.edit_field_type_end_divider)' ).forEach(
1076 function( field ) {
1077 wrapFieldLiInPlace( field );
1078 }
1079 );
1080 }
1081
1082 function deleteEmptyDividerWrappers() {
1083 var dividers = document.querySelectorAll( 'ul.start_divider' );
1084 if ( ! dividers.length ) {
1085 return;
1086 }
1087 dividers.forEach(
1088 function( divider ) {
1089 var children = [].slice.call( divider.children );
1090 children.forEach(
1091 function( child ) {
1092 if ( 0 === child.children.length ) {
1093 child.remove();
1094 } else if ( 1 === child.children.length && 'ul' === child.firstElementChild.nodeName.toLowerCase() && 0 === child.firstElementChild.children.length ) {
1095 child.remove();
1096 }
1097 }
1098 );
1099 }
1100 );
1101 }
1102
1103 /**
1104 * @returns {bool} true if the placeholder parent should be fixed.
1105 */
1106 function shouldTryFixingPlaceholderParent( $placeholder ) {
1107 var closestSection = $placeholder.closest( '.start_divider' );
1108 if ( null === closestSection ) {
1109 return false;
1110 }
1111 return jQuery( closestSection ).siblings( 'li.start_divider' ).length >= 1;
1112 }
1113
1114 function getFieldsInRow( $row ) {
1115 return $row.children( 'li.form-field' ).not( '.ui-sortable-helper' ).not( '.edit_field_type_end_divider' ).filter(
1116 function() {
1117 return 'none' !== this.style.display;
1118 }
1119 );
1120 }
1121
1122 function determineIndexBasedOffOfMousePositionInRow( $row, x ) {
1123 var $inputs = getFieldsInRow( $row ),
1124 length = $inputs.length,
1125 index, input, inputLeft, returnIndex;
1126
1127 returnIndex = 0;
1128 for ( index = length - 1; index >= 0; --index ) {
1129 input = $inputs.get( index );
1130 inputLeft = jQuery( input ).offset().left;
1131 if ( x > inputLeft ) {
1132 returnIndex = index;
1133 if ( x > inputLeft + ( jQuery( input ).outerWidth() / 2 ) ) {
1134 returnIndex = index + 1;
1135 }
1136 break;
1137 }
1138 }
1139
1140 return returnIndex;
1141 }
1142
1143 function syncLayoutClasses( $item, type ) {
1144 var $fields, size, layoutClasses, classToAddFunction;
1145
1146 if ( 'undefined' === typeof type ) {
1147 type = 'even';
1148 }
1149
1150 $fields = $item.parent().children( 'li.form-field, li.frmbutton_loadingnow' ).not( '.edit_field_type_end_divider' );
1151 size = $fields.length;
1152 layoutClasses = getLayoutClasses();
1153
1154 if ( 'even' === type && 5 !== size ) {
1155 $fields.each( getSyncLayoutClass( layoutClasses, getEvenClassForSize( size ) ) );
1156 } else if ( 'clear' === type ) {
1157 $fields.each( getSyncLayoutClass( layoutClasses, '' ) );
1158 } else {
1159 if ( -1 !== [ 'left', 'right', 'middle', 'even' ].indexOf( type ) ) {
1160 classToAddFunction = function( index ) {
1161 return getClassForBlock( size, type, index );
1162 };
1163 } else {
1164 classToAddFunction = function( index ) {
1165 var size = type[ index ];
1166 return getLayoutClassForSize( size );
1167 };
1168 }
1169
1170 $fields.each( getSyncLayoutClass( layoutClasses, classToAddFunction ) );
1171 }
1172
1173 updateFieldGroupControls( $item.parent(), $fields.length );
1174 }
1175
1176 function updateFieldGroupControls( $row, count ) {
1177 var rowOffset, shouldShowControls, controls;
1178
1179 rowOffset = $row.offset();
1180
1181 if ( 'undefined' === typeof rowOffset ) {
1182 return;
1183 }
1184
1185 shouldShowControls = count >= 2;
1186
1187 controls = document.getElementById( 'frm_field_group_controls' );
1188 if ( null === controls ) {
1189 if ( ! shouldShowControls ) {
1190 // exit early. if we do not need controls and they do not exist, do nothing.
1191 return;
1192 }
1193
1194 controls = div();
1195 controls.id = 'frm_field_group_controls';
1196 controls.setAttribute( 'role', 'group' );
1197 controls.setAttribute( 'tabindex', 0 );
1198 setFieldControlsHtml( controls );
1199 document.getElementById( 'frm_builder_page' ).appendChild( controls );
1200 }
1201
1202 $row.append( controls );
1203 controls.style.display = shouldShowControls ? 'block' : 'none';
1204 }
1205
1206 function setFieldControlsHtml( controls ) {
1207 var layoutOption, moveOption;
1208
1209 layoutOption = document.createElement( 'span' );
1210 layoutOption.innerHTML = '<svg class="frmsvg"><use xlink:href="#frm_field_group_layout_icon"></use></svg>';
1211 const layoutOptionLabel = __( 'Set Row Layout', 'formidable' );
1212 addTooltip( layoutOption, layoutOptionLabel );
1213 makeTabbable( layoutOption, layoutOptionLabel );
1214
1215 moveOption = document.createElement( 'span' );
1216 moveOption.innerHTML = '<svg class="frmsvg"><use xlink:href="#frm_thick_move_icon"></use></svg>';
1217 moveOption.classList.add( 'frm-move' );
1218 const moveOptionLabel = __( 'Move Field Group', 'formidable' );
1219 addTooltip( moveOption, moveOptionLabel );
1220 makeTabbable( moveOption, moveOptionLabel );
1221
1222 controls.innerHTML = '';
1223 controls.appendChild( layoutOption );
1224 controls.appendChild( moveOption );
1225 controls.appendChild( getFieldControlsDropdown() );
1226 }
1227
1228 function addTooltip( element, title ) {
1229 element.setAttribute( 'data-toggle', 'tooltip' );
1230 element.setAttribute( 'data-container', 'body' );
1231 element.setAttribute( 'title', title );
1232 element.addEventListener(
1233 'mouseover',
1234 function() {
1235 if ( null === element.getAttribute( 'data-original-title' ) ) {
1236 jQuery( element ).tooltip();
1237 }
1238 }
1239 );
1240 }
1241
1242 function getFieldControlsDropdown() {
1243 var dropdown, trigger, ul;
1244
1245 dropdown = document.createElement( 'span' );
1246 dropdown.classList.add( 'dropdown' );
1247
1248 trigger = document.createElement( 'a' );
1249 trigger.classList.add( 'frm_bstooltip', 'frm-hover-icon', 'frm-dropdown-toggle', 'dropdown-toggle' );
1250 trigger.setAttribute( 'title', __( 'More Options', 'formidable' ) );
1251 trigger.setAttribute( 'data-toggle', 'dropdown' );
1252 trigger.setAttribute( 'data-container', 'body' );
1253 makeTabbable( trigger, __( 'More Options', 'formidable' ) );
1254 trigger.innerHTML = '<span><svg class="frmsvg"><use xlink:href="#frm_thick_more_vert_icon"></use></svg></span>';
1255 dropdown.appendChild( trigger );
1256
1257 ul = document.createElement( 'div' );
1258 ul.classList.add( 'frm-dropdown-menu', 'dropdown-menu' );
1259 ul.setAttribute( 'role', 'menu' );
1260 dropdown.appendChild( ul );
1261
1262 return dropdown;
1263 }
1264
1265 function getSyncLayoutClass( layoutClasses, classToAdd ) {
1266 return function( itemIndex ) {
1267 var currentClassToAdd, length, layoutClassIndex, currentClass, activeLayoutClass, fieldId, layoutClassesInput;
1268
1269 currentClassToAdd = 'function' === typeof classToAdd ? classToAdd( itemIndex ) : classToAdd;
1270 length = layoutClasses.length;
1271 activeLayoutClass = false;
1272 for ( layoutClassIndex = 0; layoutClassIndex < length; ++layoutClassIndex ) {
1273 currentClass = layoutClasses[ layoutClassIndex ];
1274 if ( this.classList.contains( currentClass ) ) {
1275 activeLayoutClass = currentClass;
1276 break;
1277 }
1278 }
1279
1280 fieldId = this.dataset.fid;
1281
1282 if ( 'undefined' === typeof fieldId ) {
1283 // we are syncing the drag/drop placeholder before the actual field has loaded.
1284 // this will get called again afterward and the input will exist then.
1285 this.classList.add( currentClassToAdd );
1286 return;
1287 }
1288
1289 moveFieldSettings( document.getElementById( 'frm-single-settings-' + fieldId ) );
1290 layoutClassesInput = document.getElementById( 'frm_classes_' + fieldId );
1291
1292 if ( null === layoutClassesInput ) {
1293 // not every field type has a layout class input.
1294 return;
1295 }
1296
1297 if ( false === activeLayoutClass ) {
1298 if ( '' !== currentClassToAdd ) {
1299 layoutClassesInput.value = layoutClassesInput.value.concat( ' ' + currentClassToAdd );
1300 }
1301 } else {
1302 this.classList.remove( activeLayoutClass );
1303 layoutClassesInput.value = layoutClassesInput.value.replace( activeLayoutClass, currentClassToAdd );
1304 }
1305
1306 if ( this.classList.contains( 'frm_first' ) ) {
1307 this.classList.remove( 'frm_first' );
1308 layoutClassesInput.value = layoutClassesInput.value.replace( 'frm_first', '' ).trim();
1309 }
1310
1311 if ( 0 === itemIndex ) {
1312 this.classList.add( 'frm_first' );
1313 layoutClassesInput.value = layoutClassesInput.value.concat( ' frm_first' );
1314 }
1315
1316 jQuery( layoutClassesInput ).trigger( 'change' );
1317 };
1318 }
1319
1320 function getLayoutClasses() {
1321 return [ 'frm_full', 'frm_half', 'frm_third', 'frm_fourth', 'frm_sixth', 'frm_two_thirds', 'frm_three_fourths', 'frm1', 'frm2', 'frm3', 'frm4', 'frm5', 'frm6', 'frm7', 'frm8', 'frm9', 'frm10', 'frm11', 'frm12' ];
1322 }
1323
1324 function setupFieldOptionSorting( sort ) {
1325 var opts = {
1326 items: '.frm_sortable_field_opts li',
1327 axis: 'y',
1328 opacity: 0.65,
1329 forcePlaceholderSize: false,
1330 handle: '.frm-drag',
1331 helper: function( e, li ) {
1332 copyHelper = li.clone().insertAfter( li );
1333 return li.clone();
1334 },
1335 stop: function( e, ui ) {
1336 copyHelper && copyHelper.remove();
1337 var fieldId = ui.item.attr( 'id' ).replace( 'frm_delete_field_', '' ).replace( '-' + ui.item.data( 'optkey' ) + '_container', '' );
1338 resetDisplayedOpts( fieldId );
1339 }
1340 };
1341
1342 jQuery( sort ).sortable( opts );
1343 }
1344
1345 // Get the section where a field is dropped
1346 function getSectionForFieldPlacement( currentItem ) {
1347 var section = '';
1348 if ( typeof currentItem !== 'undefined' && ! currentItem.hasClass( 'edit_field_type_divider' ) ) {
1349 section = currentItem.closest( '.edit_field_type_divider' );
1350 }
1351 return section;
1352 }
1353
1354 // Get the form ID where a field is dropped
1355 function getFormIdForFieldPlacement( section ) {
1356 var formId = '';
1357
1358 if ( typeof section[0] !== 'undefined' ) {
1359 var sDivide = section.children( '.start_divider' );
1360 sDivide.children( '.edit_field_type_end_divider' ).appendTo( sDivide );
1361 if ( typeof section.attr( 'data-formid' ) !== 'undefined' ) {
1362 var fieldId = section.attr( 'data-fid' );
1363 formId = jQuery( 'input[name="field_options[form_select_' + fieldId + ']"]' ).val();
1364 }
1365 }
1366
1367 if ( typeof formId === 'undefined' || formId === '' ) {
1368 formId = thisFormId;
1369 }
1370
1371 return formId;
1372 }
1373
1374 // Get the section ID where a field is dropped
1375 function getSectionIdForFieldPlacement( section ) {
1376 var sectionId = 0;
1377 if ( typeof section[0] !== 'undefined' ) {
1378 sectionId = section.attr( 'id' ).replace( 'frm_field_id_', '' );
1379 }
1380
1381 return sectionId;
1382 }
1383
1384 /**
1385 * Update a field after it is dragged and dropped into, out of, or between sections
1386 *
1387 * @param {object} currentItem
1388 */
1389 function updateFieldAfterMovingBetweenSections( currentItem ) {
1390 var fieldId, section, formId, sectionId;
1391
1392 fieldId = currentItem.attr( 'id' ).replace( 'frm_field_id_', '' );
1393 section = getSectionForFieldPlacement( currentItem );
1394 formId = getFormIdForFieldPlacement( section );
1395 sectionId = getSectionIdForFieldPlacement( section );
1396
1397 if ( currentItem.parent().hasClass( 'start_divider' ) ) {
1398 wrapFieldLiInPlace( currentItem );
1399 }
1400
1401 currentItem[0].addEventListener( 'click', function() {
1402 maybeAddSaveAndDragIcons( this.dataset.fid );
1403 });
1404
1405 jQuery.ajax({
1406 type: 'POST', url: ajaxurl,
1407 data: {
1408 action: 'frm_update_field_after_move',
1409 form_id: formId,
1410 field: fieldId,
1411 section_id: sectionId,
1412 nonce: frmGlobal.nonce
1413 },
1414 success: function() {
1415 toggleSectionHolder();
1416 updateInSectionValue( fieldId, sectionId );
1417 }
1418 });
1419 }
1420
1421 // Update the in_section field value
1422 function updateInSectionValue( fieldId, sectionId ) {
1423 document.getElementById( 'frm_in_section_' + fieldId ).value = sectionId;
1424 }
1425
1426 /**
1427 * Add a new field by dragging and dropping it from the Fields sidebar
1428 *
1429 * @param {object} selectedItem
1430 * @param {object} fieldButton
1431 * @param {object} opts
1432 */
1433 function insertNewFieldByDragging( selectedItem, fieldButton ) {
1434 const fieldType = fieldButton.attr( 'id' );
1435
1436 const sortableData = jQuery( selectedItem ).data().uiSortable;
1437 const currentItem = sortableData.currentItem;
1438 const insertAtIndex = determineIndexBasedOffOfMousePositionInRow( currentItem.parent(), currentItem.offset().left );
1439 jQuery( getFieldsInRow( currentItem.parent() ).get( insertAtIndex ) ).before( currentItem );
1440 const section = getSectionForFieldPlacement( currentItem );
1441 const formId = getFormIdForFieldPlacement( section );
1442 const sectionId = getSectionIdForFieldPlacement( section );
1443
1444 const loadingID = fieldType.replace( '|', '-' ) + '_' + getAutoId();
1445 const $placeholder = jQuery( '<li class="frm-wait frmbutton_loadingnow" id="' + loadingID + '" ></li>' );
1446 currentItem.replaceWith( $placeholder );
1447
1448 syncLayoutClasses( $placeholder );
1449
1450 let hasBreak = 0;
1451 if ( 'summary' === fieldType ) {
1452 // see if we need to insert a page break before this newly-added summary field. Check for at least 1 page break
1453 hasBreak = jQuery( '.frmbutton_loadingnow#' + loadingID ).prevAll( 'li[data-type="break"]' ).length ? 1 : 0;
1454 }
1455
1456 jQuery.ajax({
1457 type: 'POST', url: ajaxurl,
1458 data: {
1459 action: 'frm_insert_field',
1460 form_id: formId,
1461 field_type: fieldType,
1462 section_id: sectionId,
1463 nonce: frmGlobal.nonce,
1464 has_break: hasBreak
1465 },
1466 success: function( msg ) {
1467 let replaceWith;
1468 document.getElementById( 'frm_form_editor_container' ).classList.add( 'frm-has-fields' );
1469 const $siblings = $placeholder.siblings( 'li.form-field' ).not( '.edit_field_type_end_divider' );
1470 if ( ! $siblings.length ) {
1471 // if dragging into a new row, we need to wrap the li first.
1472 replaceWith = wrapFieldLi( msg );
1473 } else {
1474 replaceWith = msg;
1475 }
1476 $placeholder.replaceWith( replaceWith );
1477 updateFieldOrder();
1478 afterAddField( msg, false );
1479 if ( $siblings.length ) {
1480 syncLayoutClasses( $siblings.first() );
1481 }
1482 toggleSectionHolder();
1483 },
1484 error: handleInsertFieldError
1485 });
1486 }
1487
1488 function handleInsertFieldError( jqXHR, _, errorThrown ) {
1489 maybeShowInsertFieldError( errorThrown, jqXHR );
1490 }
1491
1492 function maybeShowInsertFieldError( errorThrown, jqXHR ) {
1493 if ( ! jqXHRAborted( jqXHR ) ) {
1494 infoModal( errorThrown + '. Please try again.' );
1495 }
1496 }
1497
1498 function jqXHRAborted( jqXHR ) {
1499 return jqXHR.status === 0 || jqXHR.readyState === 0;
1500 }
1501
1502 /**
1503 * Get a unique id that automatically increments with every function call.
1504 * Can be used for any UI that requires a unique id.
1505 * Not to be used in data.
1506 *
1507 * @returns {integer}
1508 */
1509 function getAutoId() {
1510 return ++autoId;
1511 }
1512
1513 // don't allow page break, embed form, or section inside section field
1514 // don't allow page breaks inside of field groups.
1515 // don't allow field groups with sections inside of sections.
1516 // don't allow field groups in field groups.
1517 // don't allow hidden fields inside of field groups but allow them in sections.
1518 function allowDrop( ui ) {
1519 var fieldsInRow, insideFieldGroup, insideSection, isNewField, isPageBreak, isFieldGroup, isSection;
1520
1521 fieldsInRow = getFieldsInRow( ui.placeholder.parent() );
1522
1523 if ( ! groupCanFitAnotherField( fieldsInRow, ui.item ) ) {
1524 return false;
1525 }
1526
1527 insideFieldGroup = fieldsInRow.length > 0;
1528 insideSection = ui.placeholder.closest( '.start_divider' ).length > 0;
1529
1530 if ( ! insideSection && ! insideFieldGroup ) {
1531 return true;
1532 }
1533
1534 if ( insideFieldGroup && ui.placeholder.siblings( '.edit_field_type_break, .edit_field_type_hidden' ).length ) {
1535 // never allow any field beside a page break or a hidden field.
1536 return false;
1537 }
1538
1539 if ( insideSection && ui.placeholder.siblings().length > 1 && ui.placeholder.prev().hasClass( 'edit_field_type_end_divider' ) ) {
1540 return false;
1541 }
1542
1543 isNewField = ui.item.hasClass( 'frmbutton' );
1544
1545 if ( isNewField ) {
1546 isPageBreak = ui.item.hasClass( 'frm_tbreak' );
1547
1548 if ( isPageBreak ) {
1549 // do not allow page break in both sections and field groups.
1550 return false;
1551 }
1552
1553 if ( insideFieldGroup && ( ui.item.hasClass( 'frm_thidden' ) || ui.item.hasClass( 'frm_tsummary' ) ) ) {
1554 // do not allow a hidden field or summary field in a field group.
1555 return false;
1556 }
1557
1558 if ( ! insideSection ) {
1559 return true;
1560 }
1561
1562 return ! ui.item.hasClass( 'frm_tform' ) && ! ui.item.hasClass( 'frm_tdivider' ) && ! ui.item.hasClass( 'frm_tdivider-repeat' );
1563 }
1564
1565 isPageBreak = ui.item.hasClass( 'edit_field_type_break' );
1566
1567 if ( isPageBreak ) {
1568 // do not allow page break in both sections and field groups.
1569 return false;
1570 }
1571
1572 isFieldGroup = ui.item.find( 'ul.frm_sorting' ).length > 0;
1573 isSection = ui.item.hasClass( 'edit_field_type_divider' );
1574
1575 if ( insideSection && isSection ) {
1576 // but do not allow a section inside of a section.
1577 return false;
1578 }
1579
1580 if ( isFieldGroup && insideFieldGroup ) {
1581 // allow a field group inside of a field group if it is being placed within a section above/below another field group.
1582 return insideSection && ui.placeholder.siblings( 'li.edit_field_type_end_divider' ).length > 0;
1583 }
1584
1585 if ( insideFieldGroup && ui.item.hasClass( 'edit_field_type_hidden' ) ) {
1586 // do not allow a hidden field inside of a field group.
1587 return false;
1588 }
1589
1590 if ( ! insideSection ) {
1591 return true;
1592 }
1593
1594 if ( ui.item.find( '.edit_field_type_divider' ).length ) {
1595 // if we are dragging a field group with a section, do not allow it in section.
1596 return false;
1597 }
1598
1599 // moving an existing field
1600 return ! ui.item.hasClass( 'edit_field_type_form' ) && ! isSection;
1601 }
1602
1603 function groupCanFitAnotherField( fieldsInRow, $field ) {
1604 var fieldId;
1605 if ( fieldsInRow.length < 6 ) {
1606 return true;
1607 }
1608 if ( fieldsInRow.length > 6 ) {
1609 return false;
1610 }
1611 fieldId = $field.attr( 'data-fid' );
1612 // allow 6 if we're not changing field groups.
1613 return 1 === jQuery( fieldsInRow ).filter( '[data-fid="' + fieldId + '"]' ).length;
1614 }
1615
1616 function loadFields( fieldId ) {
1617 var addHtmlToField, nextElement,
1618 thisField = document.getElementById( fieldId ),
1619 $thisField = jQuery( thisField ),
1620 field = [];
1621
1622 addHtmlToField = function( element ) {
1623 var frmHiddenFdata = element.querySelector( '.frm_hidden_fdata' );
1624 element.classList.add( 'frm_load_now' );
1625 if ( frmHiddenFdata !== null ) {
1626 field.push( frmHiddenFdata.innerHTML );
1627 }
1628 };
1629
1630 nextElement = thisField;
1631 addHtmlToField( nextElement );
1632 while ( nextElement.nextElementSibling && field.length < 15 ) {
1633 addHtmlToField( nextElement.nextElementSibling );
1634 nextElement = nextElement.nextElementSibling;
1635 }
1636
1637 jQuery.ajax({
1638 type: 'POST', url: ajaxurl,
1639 data: {
1640 action: 'frm_load_field',
1641 field: field,
1642 form_id: thisFormId,
1643 nonce: frmGlobal.nonce
1644 },
1645 success: function( html ) {
1646 var key, $nextSet;
1647
1648 html = html.replace( /^\s+|\s+$/g, '' );
1649 if ( html.indexOf( '{' ) !== 0 ) {
1650 jQuery( '.frm_load_now' ).removeClass( '.frm_load_now' ).html( 'Error' );
1651 return;
1652 }
1653
1654 html = JSON.parse( html );
1655
1656 for ( key in html ) {
1657 jQuery( '#frm_field_id_' + key ).replaceWith( html[key]);
1658 setupSortable( '#frm_field_id_' + key + '.edit_field_type_divider ul.frm_sorting' );
1659 }
1660
1661 $nextSet = $thisField.nextAll( '.frm_field_loading:not(.frm_load_now)' );
1662 if ( $nextSet.length ) {
1663 loadFields( $nextSet.attr( 'id' ) );
1664 } else {
1665 // go up a level
1666 $nextSet = jQuery( document.getElementById( 'frm-show-fields' ) ).find( '.frm_field_loading:not(.frm_load_now)' );
1667 if ( $nextSet.length ) {
1668 loadFields( $nextSet.attr( 'id' ) );
1669 }
1670 }
1671
1672 initiateMultiselect();
1673 renumberPageBreaks();
1674 maybeHideQuantityProductFieldOption();
1675
1676 const loadedEvent = new Event( 'frm_ajax_loaded_field', { bubbles: false });
1677 loadedEvent.frmFields = field.map( f => JSON.parse( f ) );
1678 document.dispatchEvent( loadedEvent );
1679 }
1680 });
1681 }
1682
1683 function addFieldClick() {
1684 /*jshint validthis:true */
1685 const $thisObj = jQuery( this );
1686 // there is no real way to disable a <a> (with a valid href attribute) in HTML - https://css-tricks.com/how-to-disable-links/
1687 if ( $thisObj.hasClass( 'disabled' ) ) {
1688 return false;
1689 }
1690
1691 const $button = $thisObj.closest( '.frmbutton' );
1692 const fieldType = $button.attr( 'id' );
1693
1694 let hasBreak = 0;
1695 if ( 'summary' === fieldType ) {
1696 hasBreak = $newFields.children( 'li[data-type="break"]' ).length > 0 ? 1 : 0;
1697 }
1698
1699 const formId = thisFormId;
1700 jQuery.ajax({
1701 type: 'POST',
1702 url: ajaxurl,
1703 data: {
1704 action: 'frm_insert_field',
1705 form_id: formId,
1706 field_type: fieldType,
1707 section_id: 0,
1708 nonce: frmGlobal.nonce,
1709 has_break: hasBreak
1710 },
1711 success: function( msg ) {
1712 document.getElementById( 'frm_form_editor_container' ).classList.add( 'frm-has-fields' );
1713 $newFields.append( wrapFieldLi( msg ) );
1714 afterAddField( msg, true );
1715 },
1716 error: handleInsertFieldError
1717 });
1718 return false;
1719 }
1720
1721 function maybeHideQuantityProductFieldOption() {
1722 var hide = true,
1723 opts = document.querySelectorAll( '.frmjs_prod_field_opt_cont' );
1724
1725 if ( $newFields.find( 'li.edit_field_type_product' ).length > 1 ) {
1726 hide = false;
1727 }
1728
1729 for ( var i = 0; i < opts.length; i++ ) {
1730 if ( hide ) {
1731 opts[ i ].classList.add( 'frm_hidden' );
1732 } else {
1733 opts[ i ].classList.remove( 'frm_hidden' );
1734 }
1735 }
1736 }
1737
1738 function duplicateField() {
1739 var $field, fieldId, children, newRowId, fieldOrder;
1740
1741 $field = jQuery( this ).closest( 'li.form-field' );
1742
1743 if ( $field.hasClass( 'frm-section-collapsed' ) || $field.hasClass( 'frm-page-collapsed' ) ) {
1744 return false;
1745 }
1746
1747 closeOpenFieldDropdowns();
1748
1749 fieldId = $field.data( 'fid' );
1750 children = fieldsInSection( fieldId );
1751 newRowId = this.getAttribute( 'frm-target-row-id' );
1752
1753 if ( null !== newRowId ) {
1754 fieldOrder = this.getAttribute( 'frm-field-order' );
1755 }
1756
1757 jQuery.ajax({
1758 type: 'POST',
1759 url: ajaxurl,
1760 data: {
1761 action: 'frm_duplicate_field',
1762 field_id: fieldId,
1763 form_id: thisFormId,
1764 children: children,
1765 nonce: frmGlobal.nonce
1766 },
1767 success: function( msg ) {
1768 var newRow;
1769
1770 if ( null !== newRowId ) {
1771 newRow = document.getElementById( newRowId );
1772 if ( null !== newRow ) {
1773 jQuery( newRow ).append( msg );
1774 if ( null !== fieldOrder ) {
1775 newRow.lastElementChild.setAttribute( 'frm-field-order', fieldOrder );
1776 }
1777 jQuery( newRow ).trigger(
1778 'frm_added_duplicated_field_to_row',
1779 {
1780 duplicatedFieldHtml: msg,
1781 originalFieldId: fieldId
1782 }
1783 );
1784 afterAddField( msg, false );
1785 return;
1786 }
1787 }
1788
1789 if ( $field.siblings( 'li.form-field' ).length ) {
1790 $field.after( msg );
1791 syncLayoutClasses( $field );
1792 } else {
1793 $field.parent().parent().after( wrapFieldLi( msg ) );
1794 }
1795
1796 updateFieldOrder();
1797 afterAddField( msg, false );
1798 maybeDuplicateUnsavedSettings( fieldId, msg );
1799 }
1800 });
1801 return false;
1802 }
1803
1804 function maybeDuplicateUnsavedSettings( originalFieldId, newFieldHtml ) {
1805 var originalSettings, newFieldId, copySettings, fieldOptionKeys, originalDefault, copyDefault;
1806
1807 originalSettings = document.getElementById( 'frm-single-settings-' + originalFieldId );
1808 if ( null === originalSettings ) {
1809 return;
1810 }
1811
1812 newFieldId = jQuery( newFieldHtml ).attr( 'data-fid' );
1813 if ( 'undefined' === typeof newFieldId ) {
1814 return;
1815 }
1816
1817 copySettings = document.getElementById( 'frm-single-settings-' + newFieldId );
1818 if ( null === copySettings ) {
1819 return;
1820 }
1821
1822 fieldOptionKeys = [
1823 'name', 'required', 'unique', 'read_only', 'placeholder', 'description', 'size', 'max', 'format', 'prepend', 'append', 'separate_value'
1824 ];
1825
1826 originalSettings.querySelectorAll( 'input[name^="field_options["], textarea[name^="field_options["]' ).forEach(
1827 function( originalSetting ) {
1828 var key, tagType, copySetting;
1829
1830 key = getKeyFromSettingInput( originalSetting );
1831
1832 if ( 'options' === key ) {
1833 copyOption( originalSetting, copySettings, originalFieldId, newFieldId );
1834 return;
1835 }
1836
1837 if ( -1 === fieldOptionKeys.indexOf( key ) ) {
1838 return;
1839 }
1840
1841 tagType = originalSetting.matches( 'input' ) ? 'input' : 'textarea';
1842 copySetting = copySettings.querySelector( tagType + '[name="field_options[' + key + '_' + newFieldId + ']"]' );
1843 if ( null === copySetting ) {
1844 return;
1845 }
1846
1847 if ( 'checkbox' === originalSetting.type ) {
1848 if ( originalSetting.checked !== copySetting.checked ) {
1849 jQuery( copySetting ).trigger( 'click' );
1850 }
1851 } else if ( 'text' === originalSetting.type || 'textarea' === tagType ) {
1852 if ( originalSetting.value !== copySetting.value ) {
1853 copySetting.value = originalSetting.value;
1854 jQuery( copySetting ).trigger( 'change' );
1855 }
1856 }
1857 }
1858 );
1859
1860 originalDefault = originalSettings.querySelector( 'input[name="default_value_' + originalFieldId + '"]' );
1861 if ( null !== originalDefault ) {
1862 copyDefault = copySettings.querySelector( 'input[name="default_value_' + newFieldId + '"]' );
1863 if ( null !== copyDefault && originalDefault.value !== copyDefault.value ) {
1864 copyDefault.value = originalDefault.value;
1865 jQuery( copyDefault ).trigger( 'change' );
1866 }
1867 }
1868 }
1869
1870 function copyOption( originalSetting, copySettings, originalFieldId, newFieldId ) {
1871 var remainingKeyDetails, copyKey, copySetting;
1872 remainingKeyDetails = originalSetting.name.substr( 23 + ( '' + originalFieldId ).length );
1873 copyKey = 'field_options[options_' + newFieldId + ']' + remainingKeyDetails;
1874 copySetting = copySettings.querySelector( 'input[name="' + copyKey + '"]' );
1875 if ( null !== copySetting && copySetting.value !== originalSetting.value ) {
1876 copySetting.value = originalSetting.value;
1877 jQuery( copySetting ).trigger( 'change' );
1878 }
1879 }
1880
1881 function getKeyFromSettingInput( input ) {
1882 var nameWithoutPrefix, nameSplit;
1883 nameWithoutPrefix = input.name.substr( 14 );
1884 nameSplit = nameWithoutPrefix.split( '_' );
1885 nameSplit.pop();
1886 return nameSplit.join( '_' );
1887 }
1888
1889 function closeOpenFieldDropdowns() {
1890 var openSettings = document.querySelector( '.frm-field-settings-open' );
1891 if ( null !== openSettings ) {
1892 openSettings.classList.remove( 'frm-field-settings-open' );
1893 jQuery( document ).off( 'click', '#frm_builder_page', handleClickOutsideOfFieldSettings );
1894 jQuery( '.frm-field-action-icons .dropdown.open' ).removeClass( 'open' );
1895 }
1896 }
1897
1898 function handleClickOutsideOfFieldSettings( event ) {
1899 if ( ! jQuery( event.originalEvent.target ).closest( '.frm-field-action-icons' ).length ) {
1900 closeOpenFieldDropdowns();
1901 }
1902 }
1903
1904 function checkForMultiselectKeysOnMouseMove( event ) {
1905 var keyIsDown = ! ! ( event.ctrlKey || event.metaKey || event.shiftKey );
1906 jQuery( document.getElementById( 'frm_builder_page' ) ).toggleClass( 'frm-multiselect-key-is-down', keyIsDown );
1907 checkForActiveHoverTarget( event );
1908 }
1909
1910 function checkForActiveHoverTarget( event ) {
1911 var container, elementFromPoint, list, previousHoverTarget;
1912
1913 container = document.getElementById( 'post-body-content' );
1914 if ( container.classList.contains( 'frm-dragging-field' ) ) {
1915 return;
1916 }
1917
1918 if ( null !== document.querySelector( '.frm-field-group-hover-target .frm-field-settings-open' ) ) {
1919 // do not set a hover target if a dropdown is open for the current hover target.
1920 return;
1921 }
1922
1923 elementFromPoint = document.elementFromPoint( event.clientX, event.clientY );
1924 if ( null !== elementFromPoint && ! elementFromPoint.classList.contains( 'edit_field_type_divider' ) ) {
1925
1926 list = elementFromPoint.closest( 'ul.frm_sorting' );
1927
1928 if ( null !== list && ! list.classList.contains( 'start_divider' ) && 'frm-show-fields' !== list.id ) {
1929 previousHoverTarget = maybeRemoveGroupHoverTarget();
1930 if ( false !== previousHoverTarget && ! jQuery( previousHoverTarget ).is( list ) ) {
1931 destroyFieldGroupPopup();
1932 }
1933 updateFieldGroupControls( jQuery( list ), getFieldsInRow( jQuery( list ) ).length );
1934 list.classList.add( 'frm-field-group-hover-target' );
1935 jQuery( '#wpbody-content' ).on( 'mousemove', maybeRemoveHoverTargetOnMouseMove );
1936 }
1937 }
1938 }
1939
1940 function maybeRemoveGroupHoverTarget() {
1941 var controls, previousHoverTarget;
1942
1943 controls = document.getElementById( 'frm_field_group_controls' );
1944 if ( null !== controls ) {
1945 controls.style.display = 'none';
1946 }
1947
1948 previousHoverTarget = document.querySelector( '.frm-field-group-hover-target' );
1949 if ( null === previousHoverTarget ) {
1950 return false;
1951 }
1952
1953 jQuery( '#wpbody-content' ).off( 'mousemove', maybeRemoveHoverTargetOnMouseMove );
1954 previousHoverTarget.classList.remove( 'frm-field-group-hover-target' );
1955 return previousHoverTarget;
1956 }
1957
1958 function maybeRemoveHoverTargetOnMouseMove( event ) {
1959 var elementFromPoint = document.elementFromPoint( event.clientX, event.clientY );
1960 if ( null !== elementFromPoint && null !== elementFromPoint.closest( '#frm-show-fields' ) ) {
1961 return;
1962 }
1963 maybeRemoveGroupHoverTarget();
1964 }
1965
1966 function onFieldActionDropdownShow( isFieldGroup ) {
1967 unselectFieldGroups();
1968 // maybe offset the dropdown if it goes off of the right of the screen.
1969 setTimeout(
1970 function() {
1971 var ul, $ul;
1972 ul = document.querySelector( '.dropdown.show .frm-dropdown-menu' );
1973 if ( null === ul ) {
1974 return;
1975 }
1976 if ( null === ul.getAttribute( 'aria-label' ) ) {
1977 ul.setAttribute( 'aria-label', __( 'More Options', 'formidable' ) );
1978 }
1979 if ( 0 === ul.children.length ) {
1980 fillFieldActionDropdown( ul, true === isFieldGroup );
1981 }
1982 $ul = jQuery( ul );
1983 if ( $ul.offset().left > jQuery( window ).width() - $ul.outerWidth() ) {
1984 ul.style.left = ( -$ul.outerWidth() ) + 'px';
1985 }
1986 const firstAnchor = ul.firstElementChild.querySelector( 'a' );
1987 if ( firstAnchor ) {
1988 firstAnchor.focus();
1989 }
1990 },
1991 0
1992 );
1993 }
1994
1995 function onFieldGroupActionDropdownShow() {
1996 onFieldActionDropdownShow( true );
1997 }
1998
1999 function fillFieldActionDropdown( ul, isFieldGroup ) {
2000 var classSuffix, options;
2001 classSuffix = isFieldGroup ? '_field_group' : '_field';
2002 options = [ getDeleteActionOption( isFieldGroup ), getDuplicateActionOption( isFieldGroup ) ];
2003 if ( ! isFieldGroup ) {
2004 options.push(
2005 { class: 'frm_select', icon: 'frm_settings_icon', label: __( 'Field settings', 'formidable' ) }
2006 );
2007 }
2008 options.forEach(
2009 function( option ) {
2010 var li, anchor, span;
2011 li = document.createElement( 'div' );
2012 li.classList.add( 'frm_dropdown_li', 'frm_more_options_li', 'dropdown-item' );
2013
2014 anchor = document.createElement( 'a' );
2015 anchor.classList.add( option.class + classSuffix );
2016 anchor.setAttribute( 'href', '#' );
2017 makeTabbable( anchor );
2018
2019 span = document.createElement( 'span' );
2020 span.textContent = option.label;
2021 anchor.innerHTML = '<svg class="frmsvg"><use xlink:href="#' + option.icon + '"></use></svg>';
2022 anchor.appendChild( document.createTextNode( ' ' ) );
2023 anchor.appendChild( span );
2024
2025 li.appendChild( anchor );
2026 ul.appendChild( li );
2027 }
2028 );
2029 }
2030
2031 function getDeleteActionOption( isFieldGroup ) {
2032 var option = { class: 'frm_delete', icon: 'frm_delete_icon' };
2033 option.label = isFieldGroup ? __( 'Delete Group', 'formidable' ) : __( 'Delete', 'formidable' );
2034 return option;
2035 }
2036
2037 function getDuplicateActionOption( isFieldGroup ) {
2038 var option = { class: 'frm_clone', icon: 'frm_clone_icon' };
2039 option.label = isFieldGroup ? __( 'Duplicate Group', 'formidable' ) : __( 'Duplicate', 'formidable' );
2040 return option;
2041 }
2042
2043 function wrapFieldLi( li ) {
2044 return jQuery( '<li>' )
2045 .addClass( 'frm_field_box' )
2046 .html(
2047 jQuery( '<ul>' ).addClass( 'frm_grid_container frm_sorting' ).append( li )
2048 );
2049 }
2050
2051 function wrapFieldLiInPlace( li ) {
2052 jQuery( li ).wrap( '<li class="frm_field_box"><ul class="frm_grid_container frm_sorting"></ul></li>' );
2053 }
2054
2055 function afterAddField( msg, addFocus ) {
2056 var regex = /id="(\S+)"/,
2057 match = regex.exec( msg ),
2058 field = document.getElementById( match[1]),
2059 section = '#' + match[1] + '.edit_field_type_divider ul.frm_sorting.start_divider',
2060 $thisSection = jQuery( section ),
2061 type = field.getAttribute( 'data-type' ),
2062 toggled = false,
2063 $parentSection;
2064
2065 setupSortable( section );
2066
2067 if ( 'quantity' === type ) {
2068 // try to automatically attach a product field
2069 maybeSetProductField( field );
2070 }
2071
2072 if ( 'product' === type || 'quantity' === type ) {
2073 // quantity too needs to be a part of the if stmt especially cos of the very
2074 // 1st quantity field (or even if it's just one quantity field in the form).
2075 maybeHideQuantityProductFieldOption();
2076 }
2077
2078 if ( $thisSection.length ) {
2079 $thisSection.parent( '.frm_field_box' ).children( '.frm_no_section_fields' ).addClass( 'frm_block' );
2080 } else {
2081 $parentSection = jQuery( field ).closest( 'ul.frm_sorting.start_divider' );
2082 if ( $parentSection.length ) {
2083 toggleOneSectionHolder( $parentSection );
2084 toggled = true;
2085 }
2086 }
2087
2088 if ( msg.indexOf( 'frm-collapse-page' ) !== -1 ) {
2089 renumberPageBreaks();
2090 }
2091
2092 addClass( field, 'frm-newly-added' );
2093 setTimeout( function() {
2094 field.classList.remove( 'frm-newly-added' );
2095 }, 1000 );
2096
2097 if ( addFocus ) {
2098 var bounding = field.getBoundingClientRect(),
2099 container = document.getElementById( 'post-body-content' ),
2100 inView = ( bounding.top >= 0 &&
2101 bounding.left >= 0 &&
2102 bounding.right <= ( window.innerWidth || document.documentElement.clientWidth ) &&
2103 bounding.bottom <= ( window.innerHeight || document.documentElement.clientHeight )
2104 );
2105
2106 if ( ! inView ) {
2107 container.scroll({
2108 top: container.scrollHeight,
2109 left: 0,
2110 behavior: 'smooth'
2111 });
2112 }
2113
2114 if ( toggled === false ) {
2115 toggleOneSectionHolder( $thisSection );
2116 }
2117 }
2118
2119 deselectFields();
2120 initiateMultiselect();
2121
2122 const addedEvent = new Event( 'frm_added_field', { bubbles: false });
2123 addedEvent.frmField = field;
2124 addedEvent.frmSection = section;
2125 addedEvent.frmType = type;
2126 addedEvent.frmToggles = toggled;
2127 document.dispatchEvent( addedEvent );
2128 }
2129
2130 function clearSettingsBox( preventFieldGroups ) {
2131 jQuery( '#new_fields .frm-single-settings' ).addClass( 'frm_hidden' );
2132 jQuery( '#frm-options-panel > .frm-single-settings' ).removeClass( 'frm_hidden' );
2133 deselectFields( preventFieldGroups );
2134 }
2135
2136 function deselectFields( preventFieldGroups ) {
2137 jQuery( 'li.ui-state-default.selected' ).removeClass( 'selected' );
2138 if ( ! preventFieldGroups ) {
2139 unselectFieldGroups();
2140 }
2141 }
2142
2143 function scrollToField( field ) {
2144 var newPos = field.getBoundingClientRect().top,
2145 container = document.getElementById( 'post-body-content' );
2146
2147 if ( typeof animate === 'undefined' ) {
2148 jQuery( container ).scrollTop( newPos );
2149 } else {
2150 // TODO: smooth scroll
2151 jQuery( container ).animate({ scrollTop: newPos }, 500 );
2152 }
2153 }
2154
2155 function checkCalculationCreatedByUser() {
2156 var calculation = this.value;
2157 var warningMessage = checkMatchingParens( calculation );
2158 warningMessage += checkShortcodes( calculation, this );
2159
2160 if ( warningMessage !== '' ) {
2161 infoModal( calculation + '\n\n' + warningMessage );
2162 }
2163 }
2164
2165 /**
2166 * Checks the Detail Page slug to see if it's a reserved word and displays a message if it is.
2167 */
2168 function checkDetailPageSlug() {
2169 var slug = jQuery( '#param' ).val(),
2170 msg;
2171 slug = slug.trim().toLowerCase();
2172 if ( Array.isArray( frm_admin_js.unsafe_params ) && frm_admin_js.unsafe_params.includes( slug ) ) {
2173 msg = frm_admin_js.slug_is_reserved;
2174 msg = msg.replace( '****', addHtmlTags( slug, 'strong' ) );
2175 msg += '<br /><br />';
2176 msg += addHtmlTags( '<a href="https://codex.wordpress.org/WordPress_Query_Vars" target="_blank" class="frm-standard-link">' + frm_admin_js.reserved_words + '</a>', 'div' );
2177 infoModal( msg );
2178 }
2179 }
2180
2181 /**
2182 * Checks View filter value for params named with reserved words and displays a message if any are found.
2183 */
2184 function checkFilterParamNames() {
2185 var regEx = /\[\s*get\s*param\s*=\s*['"]?([a-zA-Z-_]+)['"]?/ig,
2186 filterValue = jQuery( this ).val(),
2187 match = regEx.exec( filterValue ),
2188 unsafeParams = '';
2189
2190 while ( match !== null ) {
2191 if ( Array.isArray( frm_admin_js.unsafe_params ) && frm_admin_js.unsafe_params.includes( match[1]) ) {
2192 if ( unsafeParams !== '' ) {
2193 unsafeParams += '", "' + match[ 1 ];
2194 } else {
2195 unsafeParams = match[ 1 ];
2196 }
2197 }
2198 match = regEx.exec( filterValue );
2199 }
2200
2201 if ( unsafeParams !== '' ) {
2202 msg = frm_admin_js.param_is_reserved;
2203 msg = msg.replace( '****', addHtmlTags( unsafeParams, 'strong' ) );
2204 msg += '<br /><br />';
2205 msg += ' <a href="https://codex.wordpress.org/WordPress_Query_Vars" target="_blank" class="frm-standard-link">' + frm_admin_js.reserved_words + '</a>';
2206
2207 infoModal( msg );
2208 }
2209 }
2210
2211 function addHtmlTags( text, tag ) {
2212 tag = tag ? tag : 'p';
2213 return '<' + tag + '>' + text + '</' + tag + '>';
2214 }
2215
2216 /**
2217 * Checks a string for parens, brackets, and curly braces and returns a message if any unmatched are found.
2218 * @param formula
2219 * @returns {string}
2220 */
2221 function checkMatchingParens( formula ) {
2222
2223 var stack = [],
2224 formulaArray = formula.split( '' ),
2225 length = formulaArray.length,
2226 opening = [ '{', '[', '(' ],
2227 closing = {
2228 '}': '{',
2229 ')': '(',
2230 ']': '['
2231 },
2232 unmatchedClosing = [],
2233 msg = '',
2234 i, top;
2235
2236 for ( i = 0; i < length; i++ ) {
2237 if ( opening.includes( formulaArray[i]) ) {
2238 stack.push( formulaArray[i]);
2239 continue;
2240 }
2241 if ( closing.hasOwnProperty( formulaArray[i]) ) {
2242 top = stack.pop();
2243 if ( top !== closing[formulaArray[i]]) {
2244 unmatchedClosing.push( formulaArray[i]);
2245 }
2246 }
2247 }
2248
2249 if ( stack.length > 0 || unmatchedClosing.length > 0 ) {
2250 msg = frm_admin_js.unmatched_parens + '\n\n';
2251 return msg;
2252 }
2253
2254 return '';
2255 }
2256
2257 /**
2258 * Checks a calculation for shortcodes that shouldn't be in it and returns a message if found.
2259 * @param calculation
2260 * @param inputElement
2261 * @returns {string}
2262 */
2263 function checkShortcodes( calculation, inputElement ) {
2264 var msg = checkNonNumericShortcodes( calculation, inputElement );
2265 msg += checkNonFormShortcodes( calculation );
2266
2267 return msg;
2268 }
2269
2270 /**
2271 * Checks if a numeric calculation has shortcodes that output non-numeric strings and returns a message if found.
2272 * @param calculation
2273 *
2274 * @param inputElement
2275 * @returns {string}
2276 */
2277 function checkNonNumericShortcodes( calculation, inputElement ) {
2278
2279 var msg = '';
2280
2281 if ( isTextCalculation( inputElement ) ) {
2282 return msg;
2283 }
2284
2285 var nonNumericShortcodes = getNonNumericShortcodes();
2286
2287 if ( nonNumericShortcodes.test( calculation ) ) {
2288 msg = frm_admin_js.text_shortcodes + '\n\n';
2289 }
2290
2291 return msg;
2292 }
2293
2294 /**
2295 * Determines if the calculation input is from a text calculation.
2296 *
2297 * @param inputElement
2298 */
2299 function isTextCalculation( inputElement ) {
2300 return jQuery( inputElement ).siblings( 'label[for^="calc_type"]' ).children( 'input' ).prop( 'checked' );
2301 }
2302
2303 /**
2304 * Returns a regular expression of shortcodes that can't be used in numeric calculations.
2305 * @returns {RegExp}
2306 */
2307 function getNonNumericShortcodes() {
2308 return /\[(date|time|email|ip)\]/;
2309 }
2310
2311 /**
2312 * Checks if a string has any shortcodes that do not belong in forms and returns a message if any are found.
2313 * @param formula
2314 * @returns {string}
2315 */
2316 function checkNonFormShortcodes( formula ) {
2317 var nonFormShortcodes = getNonFormShortcodes(),
2318 msg = '';
2319
2320 if ( nonFormShortcodes.test( formula ) ) {
2321 msg += frm_admin_js.view_shortcodes + '\n\n';
2322 }
2323
2324 return msg;
2325 }
2326
2327 /**
2328 * Returns a regular expression of shortcodes that can't be used in forms but can be used in Views, Email
2329 * Notifications, and other Formidable areas.
2330 *
2331 * @returns {RegExp}
2332 */
2333 function getNonFormShortcodes() {
2334 return /\[id\]|\[key\]|\[if\s\w+\]|\[foreach\s\w+\]|\[created-at(\s*)?/g;
2335 }
2336
2337 function isCalcBoxType( box, listClass ) {
2338 var list = jQuery( box ).find( '.frm_code_list' );
2339 return 1 === list.length && list.hasClass( listClass );
2340 }
2341
2342 function extractExcludedOptions( exclude ) {
2343 var opts = [];
2344 if ( ! Array.isArray( exclude ) ) {
2345 return opts;
2346 }
2347
2348 for ( var i = 0; i < exclude.length; i++ ) {
2349 if ( exclude[ i ].startsWith( '[' ) ) {
2350 opts.push( exclude[ i ]);
2351 // remove it
2352 exclude.splice( i, 1 );
2353 // https://love2dev.com/blog/javascript-remove-from-array/#remove-from-array-splice-value
2354 i--;
2355 }
2356 }
2357
2358 return opts;
2359 }
2360
2361 function hasExcludedOption( field, excludedOpts ) {
2362 var hasOption = false;
2363 for ( var i = 0; i < excludedOpts.length; i++ ) {
2364 var inputs = document.getElementsByName( getFieldOptionInputName( excludedOpts[ i ], field.fieldId ) );
2365 // 2nd condition checks that there's at least one non-empty value
2366 if ( inputs.length && jQuery( inputs[0]).val() ) {
2367 hasOption = true;
2368 break;
2369 }
2370 }
2371 return hasOption;
2372 }
2373
2374 function getFieldOptionInputName( opt, fieldId ) {
2375 var at = opt.indexOf( ']' );
2376 return 'field_options' + opt.substring( 0, at ) + '_' + fieldId + opt.substring( at );
2377 }
2378
2379 function popCalcFields( v, force ) {
2380 var box, exclude, fields, i, list,
2381 p = jQuery( v ).closest( '.frm-single-settings' ),
2382 calc = p.find( '.frm-calc-field' );
2383
2384 if ( ! force && ( ! calc.length || calc.val() === '' || calc.is( ':hidden' ) ) ) {
2385 return;
2386 }
2387
2388 var isSummary = isCalcBoxType( v, 'frm_js_summary_list' );
2389
2390 var fieldId = p.find( 'input[name="frm_fields_submitted[]"]' ).val();
2391
2392 if ( force ) {
2393 box = v;
2394 } else {
2395 box = document.getElementById( 'frm-calc-box-' + fieldId );
2396 }
2397
2398 exclude = getExcludeArray( box, isSummary );
2399 var excludedOpts = extractExcludedOptions( exclude );
2400
2401 fields = getFieldList();
2402 list = document.getElementById( 'frm-calc-list-' + fieldId );
2403 list.innerHTML = '';
2404
2405 for ( i = 0; i < fields.length; i++ ) {
2406 if ( ( exclude && exclude.includes( fields[ i ].fieldType ) ) ||
2407 ( excludedOpts.length && hasExcludedOption( fields[ i ], excludedOpts ) ) ) {
2408 continue;
2409 }
2410
2411 var span = document.createElement( 'span' );
2412 span.appendChild( document.createTextNode( '[' + fields[i].fieldId + ']' ) );
2413
2414 var a = document.createElement( 'a' );
2415 a.setAttribute( 'href', '#' );
2416 a.setAttribute( 'data-code', fields[i].fieldId );
2417 a.classList.add( 'frm_insert_code' );
2418 a.appendChild( span );
2419 a.appendChild( document.createTextNode( fields[i].fieldName ) );
2420
2421 var li = document.createElement( 'li' );
2422 li.classList.add( 'frm-field-list-' + fieldId );
2423 li.classList.add( 'frm-field-list-' + fields[i].fieldType );
2424 li.appendChild( a );
2425 list.appendChild( li );
2426 }
2427 }
2428
2429 function getExcludeArray( calcBox, isSummary ) {
2430 var exclude = JSON.parse( calcBox.getElementsByClassName( 'frm_code_list' )[0].getAttribute( 'data-exclude' ) );
2431
2432 if ( isSummary ) {
2433 // includedExtras are those that are normally excluded from the summary but the form owner can choose to include,
2434 // when they have been chosen to be included, then they can now be manually excluded in the calc box.
2435 var includedExtras = getIncludedExtras();
2436 if ( includedExtras.length ) {
2437 for ( var i = 0; i < exclude.length; i++ ) {
2438 if ( includedExtras.includes( exclude[ i ]) ) {
2439 // remove it
2440 exclude.splice( i, 1 );
2441 // https://love2dev.com/blog/javascript-remove-from-array/#remove-from-array-splice-value
2442 i--;
2443 }
2444 }
2445 }
2446 }
2447
2448 return exclude;
2449 }
2450
2451 function getIncludedExtras() {
2452 var checked = [];
2453 var checkboxes = document.getElementsByClassName( 'frm_include_extras_field' );
2454
2455 for ( var i = 0; i < checkboxes.length; i++ ) {
2456 if ( checkboxes[i].checked ) {
2457 checked.push( checkboxes[i].value );
2458 }
2459 }
2460
2461 return checked;
2462 }
2463
2464 function rePopCalcFieldsForSummary() {
2465 popCalcFields( jQuery( '.frm-inline-modal.postbox:has(.frm_js_summary_list)' )[0], true );
2466 }
2467
2468 function getFieldList( fieldType ) {
2469 var i,
2470 fields = [],
2471 allFields = document.querySelectorAll( 'li.frm_field_box' ),
2472 checkType = 'undefined' !== typeof fieldType;
2473
2474 for ( i = 0; i < allFields.length; i++ ) {
2475 // data-ftype is better (than data-type) cos of fields loaded by AJAX - which might not be ready yet
2476 if ( checkType && allFields[ i ].getAttribute( 'data-ftype' ) !== fieldType ) {
2477 continue;
2478 }
2479
2480 var fieldId = allFields[ i ].getAttribute( 'data-fid' );
2481 if ( typeof fieldId !== 'undefined' && fieldId ) {
2482 fields.push({
2483 'fieldId': fieldId,
2484 'fieldName': getPossibleValue( 'frm_name_' + fieldId ),
2485 'fieldType': getPossibleValue( 'field_options_type_' + fieldId ),
2486 'fieldKey': getPossibleValue( 'field_options_field_key_' + fieldId )
2487 });
2488 }
2489 }
2490
2491 return fields;
2492 }
2493
2494 function popProductFields( field ) {
2495 var i, checked, id,
2496 options = [],
2497 current = getCurrentProductFields( field ),
2498 fName = field.getAttribute( 'data-frmfname' ),
2499 products = getFieldList( 'product' ),
2500 quantities = getFieldList( 'quantity' ),
2501 isSelect = field.tagName === 'SELECT', // for reverse compatibility.
2502 // whether we have just 1 product and 1 quantity field & should therefore attach the latter to the former
2503 auto = 1 === quantities.length && 1 === products.length;
2504
2505 if ( isSelect ) {
2506 // This fallback can be removed after 4.05.
2507 current = field.getAttribute( 'data-frmcurrent' );
2508 }
2509
2510 for ( i = 0 ; i < products.length ; i++ ) {
2511 // let's be double sure it's string, else indexOf will fail
2512 id = products[ i ].fieldId.toString();
2513 checked = auto || -1 !== current.indexOf( id );
2514 if ( isSelect ) {
2515 // This fallback can be removed after 4.05.
2516 checked = checked ? ' selected' : '';
2517 options.push( '<option value="' + id + '"' + checked + '>' + products[ i ].fieldName + '</option>' );
2518 } else {
2519 checked = checked ? ' checked' : '';
2520 options.push( '<label class="frm6">' );
2521 options.push( '<input type="checkbox" name="' + fName + '" value="' + id + '"' + checked + '> ' + products[ i ].fieldName );
2522 options.push( '</label>' );
2523 }
2524 }
2525
2526 field.innerHTML = options.join( '' );
2527 }
2528
2529 function getCurrentProductFields( prodFieldOpt ) {
2530 var products = prodFieldOpt.querySelectorAll( '[type="checkbox"]:checked' ),
2531 idsArray = [];
2532
2533 for ( var i = 0; i < products.length; i++ ) {
2534 idsArray.push( products[ i ].value );
2535 }
2536
2537 return idsArray;
2538 }
2539
2540 function popAllProductFields() {
2541 var opts = document.querySelectorAll( '.frmjs_prod_field_opt' );
2542 for ( var i = 0; i < opts.length; i++ ) {
2543 popProductFields( opts[ i ]);
2544 }
2545 }
2546
2547 function maybeSetProductField( field ) {
2548 var fieldId = field.getAttribute( 'data-fid' ),
2549 productFieldOpt = document.getElementById( 'field_options[product_field_' + fieldId + ']' );
2550
2551 if ( null === productFieldOpt ) {
2552 return;
2553 }
2554
2555 popProductFields( productFieldOpt );
2556 // in order to move its settings to that LHS panel where
2557 // the update form resides, else it'll lose this setting
2558 moveFieldSettings( document.getElementById( 'frm-single-settings-' + fieldId ) );
2559 }
2560
2561 /**
2562 * If the element doesn't exist, use a blank value.
2563 */
2564 function getPossibleValue( id ) {
2565 field = document.getElementById( id );
2566 if ( field !== null ) {
2567 return field.value;
2568 } else {
2569 return '';
2570 }
2571 }
2572
2573 function liveChanges() {
2574 /*jshint validthis:true */
2575 var option,
2576 newValue = this.value,
2577 changes = document.getElementById( this.getAttribute( 'data-changeme' ) ),
2578 att = this.getAttribute( 'data-changeatt' );
2579
2580 if ( changes === null ) {
2581 return;
2582 }
2583
2584 if ( att !== null ) {
2585 if ( changes.tagName === 'SELECT' && att === 'placeholder' ) {
2586 option = changes.options[0];
2587 if ( option.value === '' ) {
2588 option.innerHTML = newValue;
2589 } else {
2590 // Create a placeholder option if there are no blank values.
2591 addBlankSelectOption( changes, newValue );
2592 }
2593 } else if ( att === 'class' ) {
2594 changeFieldClass( changes, this );
2595 } else if ( isSliderField( changes ) ) {
2596 updateSliderFieldPreview( changes, att, newValue );
2597 } else {
2598 changes.setAttribute( att, newValue );
2599 }
2600 } else if ( changes.id.indexOf( 'setup-message' ) === 0 ) {
2601 if ( newValue !== '' ) {
2602 changes.innerHTML = '<input type="text" value="" disabled />';
2603 }
2604 } else {
2605 changes.innerHTML = newValue;
2606 if ( changes.classList.contains( 'frm_primary_label' ) && 'break' === changes.nextElementSibling.getAttribute( 'data-ftype' ) ) {
2607 changes.nextElementSibling.querySelector( '.frm_button_submit' ).textContent = newValue;
2608 }
2609 }
2610 }
2611
2612 function updateSliderFieldPreview( field, att, newValue ) {
2613 if ( frmGlobal.proIncludesSliderJs ) {
2614 const hookName = 'frm_update_slider_field_preview';
2615 const hookArgs = { field, att, newValue };
2616 wp.hooks.doAction( hookName, hookArgs );
2617 return;
2618 }
2619
2620 // This functionality has been moved to pro since v5.4.3. This code should be removed eventually.
2621 if ( 'value' === att ) {
2622 if ( '' === newValue ) {
2623 newValue = getSliderMidpoint( field );
2624 }
2625 field.value = newValue;
2626 } else {
2627 field.setAttribute( att, newValue );
2628 }
2629
2630 if ( -1 === [ 'value', 'min', 'max' ].indexOf( att ) ) {
2631 return;
2632 }
2633
2634 if ( ( 'max' === att || 'min' === att ) && '' === getSliderDefaultValueInput( field.id ) ) {
2635 field.value = getSliderMidpoint( field );
2636 }
2637
2638 field.parentNode.querySelector( '.frm_range_value' ).textContent = field.value;
2639 }
2640
2641 function getSliderDefaultValueInput( previewInputId ) {
2642 return document.querySelector( 'input[data-changeme="' + previewInputId + '"][data-changeatt="value"]' ).value;
2643 }
2644
2645 function getSliderMidpoint( sliderInput ) {
2646 const max = parseFloat( sliderInput.getAttribute( 'max' ) );
2647 const min = parseFloat( sliderInput.getAttribute( 'min' ) );
2648 return ( max - min ) / 2 + min;
2649 }
2650
2651 function isSliderField( previewInput ) {
2652 return 'range' === previewInput.type && previewInput.parentNode.classList.contains( 'frm_range_container' );
2653 }
2654
2655 function toggleInvalidMsg() {
2656 /*jshint validthis:true */
2657 var typeDropdown, fieldType,
2658 fieldId = this.getAttribute( 'data-fid' ),
2659 value = '';
2660
2661 [ 'field_options_max_', 'frm_format_' ].forEach( function( id ) {
2662 var input = document.getElementById( id + fieldId );
2663 if ( ! input ) {
2664 return;
2665 }
2666
2667 value += input.value;
2668 });
2669
2670 typeDropdown = document.getElementsByName( 'field_options[type_' + fieldId + ']' )[0];
2671 fieldType = typeDropdown.options[typeDropdown.selectedIndex].value;
2672
2673 if ( fieldType === 'text' ) {
2674 toggleValidationBox( '' !== value, '.frm_invalid_msg' + fieldId );
2675 }
2676 }
2677
2678 function markRequired() {
2679 /*jshint validthis:true */
2680 var thisid = this.id.replace( 'frm_', '' ),
2681 fieldId = thisid.replace( 'req_field_', '' ),
2682 checked = this.checked,
2683 label = jQuery( '#field_label_' + fieldId + ' .frm_required' );
2684
2685 toggleValidationBox( checked, '.frm_required_details' + fieldId );
2686
2687 if ( checked ) {
2688 var $reqBox = jQuery( 'input[name="field_options[required_indicator_' + fieldId + ']"]' );
2689 if ( $reqBox.val() === '' ) {
2690 $reqBox.val( '*' );
2691 }
2692 label.removeClass( 'frm_hidden' );
2693 } else {
2694 label.addClass( 'frm_hidden' );
2695 }
2696 }
2697
2698 function toggleValidationBox( hasValue, messageClass ) {
2699 $msg = jQuery( messageClass );
2700 if ( hasValue ) {
2701 $msg.fadeIn( 'fast' ).closest( '.frm_validation_msg' ).fadeIn( 'fast' );
2702 } else {
2703 //Fade out validation options
2704 var v = $msg.fadeOut( 'fast' ).closest( '.frm_validation_box' ).children( ':not(' + messageClass + '):visible' ).length;
2705 if ( v === 0 ) {
2706 $msg.closest( '.frm_validation_msg' ).fadeOut( 'fast' );
2707 }
2708 }
2709 }
2710
2711 function markUnique() {
2712 /*jshint validthis:true */
2713 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
2714 var $thisField = jQuery( '.frm_unique_details' + fieldId );
2715 if ( this.checked ) {
2716 $thisField.fadeIn( 'fast' ).closest( '.frm_validation_msg' ).fadeIn( 'fast' );
2717 $unqDetail = jQuery( '.frm_unique_details' + fieldId + ' input' );
2718 if ( $unqDetail.val() === '' ) {
2719 $unqDetail.val( frm_admin_js.default_unique );
2720 }
2721 } else {
2722 var v = $thisField.fadeOut( 'fast' ).closest( '.frm_validation_box' ).children( ':not(.frm_unique_details' + fieldId + '):visible' ).length;
2723 if ( v === 0 ) {
2724 $thisField.closest( '.frm_validation_msg' ).fadeOut( 'fast' );
2725 }
2726 }
2727 }
2728
2729 //Fade confirmation field and validation option in or out
2730 function addConf() {
2731 /*jshint validthis:true */
2732 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
2733 var val = jQuery( this ).val();
2734 var $thisField = jQuery( document.getElementById( 'frm_field_id_' + fieldId ) );
2735
2736 toggleValidationBox( val !== '', '.frm_conf_details' + fieldId );
2737
2738 if ( val !== '' ) {
2739 //Add default validation message if empty
2740 var valMsg = jQuery( '.frm_validation_box .frm_conf_details' + fieldId + ' input' );
2741 if ( valMsg.val() === '' ) {
2742 valMsg.val( frm_admin_js.default_conf );
2743 }
2744
2745 setConfirmationFieldDescriptions( fieldId );
2746
2747 //Add or remove class for confirmation field styling
2748 if ( val === 'inline' ) {
2749 $thisField.removeClass( 'frm_conf_below' ).addClass( 'frm_conf_inline' );
2750 } else if ( val === 'below' ) {
2751 $thisField.removeClass( 'frm_conf_inline' ).addClass( 'frm_conf_below' );
2752 }
2753 jQuery( '.frm-conf-box-' + fieldId ).removeClass( 'frm_hidden' );
2754 } else {
2755 jQuery( '.frm-conf-box-' + fieldId ).addClass( 'frm_hidden' );
2756 setTimeout( function() {
2757 $thisField.removeClass( 'frm_conf_inline frm_conf_below' );
2758 }, 200 );
2759 }
2760 }
2761
2762 function setConfirmationFieldDescriptions( fieldId ) {
2763 var fieldType = document.getElementsByName( 'field_options[type_' + fieldId + ']' )[0].value;
2764
2765 var fieldDescription = document.getElementById( 'field_description_' + fieldId );
2766 var hiddenDescName = 'field_options[description_' + fieldId + ']';
2767 var newValue = frm_admin_js['enter_' + fieldType];
2768 maybeSetNewDescription( fieldDescription, hiddenDescName, newValue );
2769
2770 var confFieldDescription = document.getElementById( 'conf_field_description_' + fieldId );
2771 var hiddenConfName = 'field_options[conf_desc_' + fieldId + ']';
2772 var newConfValue = frm_admin_js['confirm_' + fieldType];
2773 maybeSetNewDescription( confFieldDescription, hiddenConfName, newConfValue );
2774 }
2775
2776 function maybeSetNewDescription( descriptionDiv, hiddenName, newValue ) {
2777 if ( descriptionDiv.innerHTML === frm_admin_js.desc ) {
2778
2779 // Set the visible description value and the hidden description value
2780 descriptionDiv.innerHTML = newValue;
2781 document.getElementsByName( hiddenName )[0].value = newValue;
2782 }
2783 }
2784
2785 function initBulkOptionsOverlay() {
2786 /*jshint validthis:true */
2787 var $info = initModal( '#frm-bulk-modal', '700px' );
2788 if ( $info === false ) {
2789 return;
2790 }
2791
2792 jQuery( '.frm-insert-preset' ).on( 'click', insertBulkPreset );
2793
2794 jQuery( builderForm ).on( 'click', 'a.frm-bulk-edit-link', function( event ) {
2795 event.preventDefault();
2796 var i, key, label,
2797 content = '',
2798 optList,
2799 opts,
2800 fieldId = jQuery( this ).closest( '[data-fid]' ).data( 'fid' ),
2801 separate = usingSeparateValues( fieldId ),
2802 product = isProductField( fieldId );
2803
2804 optList = document.getElementById( 'frm_field_' + fieldId + '_opts' );
2805 if ( ! optList ) {
2806 return;
2807 }
2808
2809 opts = optList.getElementsByTagName( 'li' );
2810
2811 document.getElementById( 'bulk-field-id' ).value = fieldId;
2812
2813 for ( i = 0; i < opts.length; i++ ) {
2814 key = opts[i].getAttribute( 'data-optkey' );
2815 if ( key !== '000' ) {
2816 label = document.getElementsByName( 'field_options[options_' + fieldId + '][' + key + '][label]' )[0];
2817 if ( typeof label !== 'undefined' ) {
2818 content += label.value;
2819 if ( separate ) {
2820 content += '|' + document.getElementsByName( 'field_options[options_' + fieldId + '][' + key + '][value]' )[0].value;
2821 }
2822 if ( product ) {
2823 content += '|' + document.getElementsByName( 'field_options[options_' + fieldId + '][' + key + '][price]' )[0].value;
2824 }
2825 content += '\r\n';
2826 }
2827 }
2828
2829 if ( i >= opts.length - 1 ) {
2830 document.getElementById( 'frm_bulk_options' ).value = content;
2831 }
2832 }
2833
2834 $info.dialog( 'open' );
2835
2836 return false;
2837 });
2838
2839 jQuery( '#frm-update-bulk-opts' ).on( 'click', function() {
2840 var fieldId = document.getElementById( 'bulk-field-id' ).value;
2841 var optionType = document.getElementById( 'bulk-option-type' ).value;
2842
2843 if ( optionType ) {
2844 // Use custom handler for custom option type.
2845 return;
2846 }
2847
2848 this.classList.add( 'frm_loading_button' );
2849 frmAdminBuild.updateOpts( fieldId, document.getElementById( 'frm_bulk_options' ).value, $info );
2850 });
2851 }
2852
2853 function insertBulkPreset( event ) {
2854 /*jshint validthis:true */
2855 var opts = JSON.parse( this.getAttribute( 'data-opts' ) );
2856 event.preventDefault();
2857 document.getElementById( 'frm_bulk_options' ).value = opts.join( '\n' );
2858 return false;
2859 }
2860
2861 //Add new option or "Other" option to radio/checkbox/dropdown
2862 function addFieldOption() {
2863 /*jshint validthis:true */
2864 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' ),
2865 newOption = jQuery( '#frm_field_' + fieldId + '_opts .frm_option_template' ).prop( 'outerHTML' ),
2866 optType = jQuery( this ).data( 'opttype' ),
2867 optKey = 0,
2868 oldKey = '000',
2869 lastKey = getHighestOptKey( fieldId );
2870
2871 if ( lastKey !== oldKey ) {
2872 optKey = lastKey + 1;
2873 }
2874
2875 //Update hidden field
2876 if ( optType === 'other' ) {
2877 document.getElementById( 'other_input_' + fieldId ).value = 1;
2878
2879 //Hide "Add Other" option now if this is radio field
2880 var ftype = jQuery( this ).data( 'ftype' );
2881 if ( ftype === 'radio' || ftype === 'select' ) {
2882 jQuery( this ).fadeOut( 'slow' );
2883 }
2884
2885 var data = {
2886 action: 'frm_add_field_option',
2887 field_id: fieldId,
2888 opt_key: optKey,
2889 opt_type: optType,
2890 nonce: frmGlobal.nonce
2891 };
2892 jQuery.post( ajaxurl, data, function( msg ) {
2893 jQuery( document.getElementById( 'frm_field_' + fieldId + '_opts' ) ).append( msg );
2894 resetDisplayedOpts( fieldId );
2895 });
2896 } else {
2897 newOption = newOption.replace( new RegExp( 'optkey="' + oldKey + '"', 'g' ), 'optkey="' + optKey + '"' );
2898 newOption = newOption.replace( new RegExp( '-' + oldKey + '_', 'g' ), '-' + optKey + '_' );
2899 newOption = newOption.replace( new RegExp( '-' + oldKey + '"', 'g' ), '-' + optKey + '"' );
2900 newOption = newOption.replace( new RegExp( '\\[' + oldKey + '\\]', 'g' ), '[' + optKey + ']' );
2901 newOption = newOption.replace( 'frm_hidden frm_option_template', '' );
2902 newOption = { newOption };
2903 addSaveAndDragIconsToOption( fieldId, newOption );
2904 jQuery( document.getElementById( 'frm_field_' + fieldId + '_opts' ) ).append( newOption.newOption );
2905 resetDisplayedOpts( fieldId );
2906 }
2907 }
2908
2909 function getHighestOptKey( fieldId ) {
2910 var i = 0,
2911 optKey = 0,
2912 opts = jQuery( '#frm_field_' + fieldId + '_opts li' ),
2913 lastKey = 0;
2914
2915 for ( i; i < opts.length; i++ ) {
2916 optKey = opts[i].getAttribute( 'data-optkey' );
2917 if ( opts.length === 1 ) {
2918 return optKey;
2919 }
2920 if ( optKey !== '000' ) {
2921 optKey = optKey.replace( 'other_', '' );
2922 optKey = parseInt( optKey, 10 );
2923 }
2924
2925 if ( ! isNaN( lastKey ) && ( optKey > lastKey || lastKey === '000' ) ) {
2926 lastKey = optKey;
2927 }
2928 }
2929
2930 return lastKey;
2931 }
2932
2933 function toggleMultSel() {
2934 /*jshint validthis:true */
2935 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
2936 toggleMultiSelect( fieldId, this.value );
2937 }
2938
2939 function toggleMultiSelect( fieldId, value ) {
2940 var setting = jQuery( '.frm_multiple_cont_' + fieldId );
2941 if ( value === 'select' ) {
2942 setting.fadeIn( 'fast' );
2943 } else {
2944 setting.fadeOut( 'fast' );
2945 }
2946 }
2947
2948 function toggleSepValues() {
2949 /*jshint validthis:true */
2950 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
2951 toggle( jQuery( '.field_' + fieldId + '_option_key' ) );
2952 jQuery( '.field_' + fieldId + '_option' ).toggleClass( 'frm_with_key' );
2953 }
2954
2955 function toggleImageOptions() {
2956 /*jshint validthis:true */
2957 var hasImageOptions, imageSize,
2958 $field = jQuery( this ).closest( '.frm-single-settings' ),
2959 fieldId = $field.data( 'fid' ),
2960 displayField = document.getElementById( 'frm_field_id_' + fieldId );
2961
2962 refreshOptionDisplayNow( jQuery( this ) );
2963
2964 toggle( jQuery( '.field_' + fieldId + '_image_id' ) );
2965 toggle( jQuery( '.frm_toggle_image_options_' + fieldId ) );
2966 toggle( jQuery( '.frm_image_size_' + fieldId ) );
2967 toggle( jQuery( '.frm_alignment_' + fieldId ) );
2968 toggle( jQuery( '.frm-add-other#frm_add_field_' + fieldId ) );
2969
2970 hasImageOptions = imagesAsOptions( fieldId );
2971
2972 if ( hasImageOptions ) {
2973 setAlignment( fieldId, 'inline' );
2974 removeImageSizeClasses( displayField );
2975 imageSize = getImageOptionSize( fieldId );
2976 displayField.classList.add( 'frm_image_options' );
2977 displayField.classList.add( 'frm_image_size_' + imageSize );
2978 $field.find( '.frm-bulk-edit-link' ).hide();
2979 } else {
2980 displayField.classList.remove( 'frm_image_options' );
2981 removeImageSizeClasses( displayField );
2982 setAlignment( fieldId, 'block' );
2983 $field.find( '.frm-bulk-edit-link' ).show();
2984 }
2985 }
2986
2987 function removeImageSizeClasses( field ) {
2988 field.classList.remove( 'frm_image_size_', 'frm_image_size_small', 'frm_image_size_medium', 'frm_image_size_large', 'frm_image_size_xlarge' );
2989 }
2990
2991 function setAlignment( fieldId, alignment ) {
2992 jQuery( '#field_options_align_' + fieldId ).val( alignment ).trigger( 'change' );
2993 }
2994
2995 function setImageSize() {
2996 var $field = jQuery( this ).closest( '.frm-single-settings' ),
2997 fieldId = $field.data( 'fid' ),
2998 displayField = document.getElementById( 'frm_field_id_' + fieldId );
2999
3000 refreshOptionDisplay();
3001
3002 if ( imagesAsOptions( fieldId ) ) {
3003 removeImageSizeClasses( displayField );
3004 displayField.classList.add( 'frm_image_options' );
3005 displayField.classList.add( 'frm_image_size_' + getImageOptionSize( fieldId ) );
3006 }
3007 }
3008
3009 function refreshOptionDisplayNow( object ) {
3010 var $field = object.closest( '.frm-single-settings' ),
3011 fieldID = $field.data( 'fid' );
3012 jQuery( '.field_' + fieldID + '_option' ).trigger( 'change' );
3013 }
3014
3015 function refreshOptionDisplay() {
3016 /*jshint validthis:true */
3017 refreshOptionDisplayNow( jQuery( this ) );
3018 }
3019
3020 function addImageToOption( event ) {
3021 const imagePreview = event.target.closest( '.frm_image_preview_wrapper' );
3022
3023 event.preventDefault();
3024
3025 wp.media.model.settings.post.id = 0;
3026
3027 const fileFrame = wp.media.frames.file_frame = wp.media({
3028 multiple: false,
3029 library: {
3030 type: [ 'image' ]
3031 }
3032 });
3033
3034 fileFrame.on( 'select', function() {
3035 const attachment = fileFrame.state().get( 'selection' ).first().toJSON();
3036 const img = imagePreview.querySelector( 'img' );
3037
3038 img.setAttribute( 'src', attachment.url );
3039 img.classList.remove( 'frm_hidden' );
3040 img.removeAttribute( 'srcset' ); // Prevent the old image from sticking around.
3041
3042 imagePreview.querySelector( '.frm_image_preview_frame' ).style.display = 'block';
3043 imagePreview.querySelector( '.frm_image_preview_title' ).textContent = attachment.filename;
3044 imagePreview.querySelector( '.frm_choose_image_box' ).style.display = 'none';
3045
3046 const $imagePreview = jQuery( imagePreview );
3047 $imagePreview.siblings( 'input[name*="[label]"]' ).data( 'frmimgurl', attachment.url );
3048 $imagePreview.find( 'input.frm_image_id' ).val( attachment.id ).trigger( 'change' );
3049 wp.media.model.settings.post.id = 0;
3050 });
3051
3052 fileFrame.open();
3053 }
3054
3055 function removeImageFromOption( event ) {
3056 var $this = jQuery( this ),
3057 previewWrapper = $this.closest( '.frm_image_preview_wrapper' );
3058
3059 event.preventDefault();
3060 event.stopPropagation();
3061
3062 previewWrapper.find( 'img' ).attr( 'src', '' );
3063 previewWrapper.find( '.frm_image_preview_frame' ).hide();
3064 previewWrapper.find( '.frm_choose_image_box' ).show();
3065 previewWrapper.find( 'input.frm_image_id' ).val( 0 ).trigger( 'change' );
3066 }
3067
3068 function toggleMultiselect() {
3069 /*jshint validthis:true */
3070 var dropdown = jQuery( this ).closest( 'li' ).find( '.frm_form_fields select' );
3071 if ( this.checked ) {
3072 dropdown.attr( 'multiple', 'multiple' );
3073 } else {
3074 dropdown.removeAttr( 'multiple' );
3075 }
3076 }
3077
3078 /**
3079 * Allow typing on form switcher click without an extra click to search.
3080 */
3081 function focusSearchBox() {
3082 var searchBox = document.getElementById( 'dropform-search-input' );
3083 if ( searchBox !== null ) {
3084 setTimeout( function() {
3085 searchBox.focus();
3086 }, 100 );
3087 }
3088 }
3089
3090 /**
3091 * If a field is clicked in the builder, prevent inputs from changing.
3092 */
3093 function stopFieldFocus( e ) {
3094 e.preventDefault();
3095 }
3096
3097 function deleteFieldOption() {
3098 /*jshint validthis:true */
3099 var otherInput,
3100 parentLi = this.parentNode,
3101 parentUl = parentLi.parentNode,
3102 fieldId = this.getAttribute( 'data-fid' );
3103
3104 jQuery( parentLi ).fadeOut( 'slow', function() {
3105 jQuery( parentLi ).remove();
3106
3107 var hasOther = jQuery( parentUl ).find( '.frm_other_option' );
3108 if ( hasOther.length < 1 ) {
3109 otherInput = document.getElementById( 'other_input_' + fieldId );
3110 if ( otherInput !== null ) {
3111 otherInput.value = 0;
3112 }
3113 jQuery( '#other_button_' + fieldId ).fadeIn( 'slow' );
3114 }
3115 });
3116 }
3117
3118 /**
3119 * If a radio button is set as default, allow a click to
3120 * deselect it.
3121 */
3122 function maybeUncheckRadio() {
3123 var $self, uncheck, unbind, up;
3124
3125 /*jshint validthis:true */
3126 $self = jQuery( this );
3127 if ( $self.is( ':checked' ) ) {
3128 uncheck = function() {
3129 setTimeout( function() {
3130 $self.prop( 'checked', false );
3131 }, 0 );
3132 };
3133 unbind = function() {
3134 $self.off( 'mouseup', up );
3135 };
3136 up = function() {
3137 uncheck();
3138 unbind();
3139 };
3140 $self.on( 'mouseup', up );
3141 $self.one( 'mouseout', unbind );
3142 }
3143 }
3144
3145 /**
3146 * If the field option has the default text, clear it out on click.
3147 */
3148 function maybeClearOptText() {
3149 /*jshint validthis:true */
3150 if ( this.value === frm_admin_js.new_option ) {
3151 this.setAttribute( 'data-value-on-focus', this.value );
3152 this.value = '';
3153 }
3154 }
3155
3156 function clickDeleteField() {
3157 /*jshint validthis:true */
3158 var confirmMsg = frm_admin_js.conf_delete,
3159 maybeDivider = this.parentNode.parentNode.parentNode.parentNode.parentNode,
3160 li = maybeDivider.parentNode,
3161 field = jQuery( this ).closest( 'li.form-field' ),
3162 fieldId = field.data( 'fid' );
3163
3164 if ( li.classList.contains( 'frm-section-collapsed' ) || li.classList.contains( 'frm-page-collapsed' ) ) {
3165 return false;
3166 }
3167
3168 // If deleting a section, use a special message.
3169 if ( maybeDivider.className === 'divider_section_only' ) {
3170 confirmMsg = frm_admin_js.conf_delete_sec;
3171 this.setAttribute( 'data-frmcaution', frm_admin_js.caution );
3172 }
3173
3174 this.setAttribute( 'data-frmverify', confirmMsg );
3175 this.setAttribute( 'data-deletefield', fieldId );
3176
3177 closeOpenFieldDropdowns();
3178
3179 confirmLinkClick( this );
3180 return false;
3181 }
3182
3183 function clickSelectField() {
3184 this.closest( 'li.form-field' ).click();
3185 }
3186
3187 function clickDeleteFieldGroup() {
3188 var hoverTarget, decoy;
3189
3190 hoverTarget = document.querySelector( '.frm-field-group-hover-target' );
3191 if ( null === hoverTarget ) {
3192 return;
3193 }
3194
3195 hoverTarget.classList.add( 'frm-selected-field-group' );
3196
3197 decoy = document.createElement( 'div' );
3198 decoy.classList.add( 'frm-delete-field-groups', 'frm_hidden' );
3199 document.body.appendChild( decoy );
3200 decoy.click();
3201 }
3202
3203 function duplicateFieldGroup() {
3204 var hoverTarget, newRowId, $newRow, $fields, syncDetails, expectedLength, duplicatedCount, originalFieldIdByDuplicatedFieldId, injectedCloneOptions;
3205
3206 hoverTarget = document.querySelector( '.frm-field-group-hover-target' );
3207
3208 if ( null === hoverTarget ) {
3209 return;
3210 }
3211
3212 newRowId = 'frm_field_group_' + getAutoId();
3213 $newRow = wrapFieldLi( '' );
3214 $newRowUl = $newRow.find( 'ul' );
3215 $newRowUl.attr( 'id', newRowId );
3216 $newRow.addClass( 'frm_hidden' );
3217 jQuery( hoverTarget ).closest( 'li.frm_field_box' ).after( $newRow );
3218
3219 $fields = getFieldsInRow( jQuery( hoverTarget ) );
3220 syncDetails = [];
3221 injectedCloneOptions = [];
3222
3223 expectedLength = $fields.length;
3224 duplicatedCount = 0;
3225 originalFieldIdByDuplicatedFieldId = {};
3226
3227 $newRow.on(
3228 'frm_added_duplicated_field_to_row',
3229 function( event, args ) {
3230 var $duplicatedFields, index;
3231
3232 originalFieldIdByDuplicatedFieldId[ jQuery( args.duplicatedFieldHtml ).attr( 'data-fid' ) ] = args.originalFieldId;
3233
3234 if ( expectedLength > ++duplicatedCount ) {
3235 return;
3236 }
3237
3238 $duplicatedFields = getFieldsInRow( $newRowUl );
3239
3240 injectedCloneOptions.forEach(
3241 function( cloneOption ) {
3242 cloneOption.remove();
3243 }
3244 );
3245
3246 for ( index = 0; index < expectedLength; ++index ) {
3247 $newRowUl.append( $newRowUl.children( 'li.form-field[frm-field-order="' + index + '"]' ) );
3248 }
3249
3250 syncLayoutClasses( $duplicatedFields.first(), syncDetails );
3251 $newRow.removeClass( 'frm_hidden' );
3252 updateFieldOrder();
3253
3254 getFieldsInRow( $newRowUl ).each(
3255 function() {
3256 maybeDuplicateUnsavedSettings( originalFieldIdByDuplicatedFieldId[ this.getAttribute( 'data-fid' ) ], jQuery( this ).prop( 'outerHTML' ) );
3257 }
3258 );
3259 }
3260 );
3261
3262 $fields.each(
3263 function( index ) {
3264 var cloneOption;
3265 cloneOption = document.createElement( 'li' );
3266 cloneOption.classList.add( 'frm_clone_field' );
3267 cloneOption.setAttribute( 'frm-target-row-id', newRowId );
3268 cloneOption.setAttribute( 'frm-field-order', index );
3269 this.appendChild( cloneOption );
3270 cloneOption.click();
3271 injectedCloneOptions.push( cloneOption );
3272 syncDetails.push( getSizeOfLayoutClass( getLayoutClassName( this.classList ) ) );
3273 }
3274 );
3275 }
3276
3277 function clickFieldGroupLayout() {
3278 var hoverTarget, sizeOfFieldGroup, popupWrapper;
3279
3280 hoverTarget = document.querySelector( '.frm-field-group-hover-target' );
3281
3282 if ( null === hoverTarget ) {
3283 return;
3284 }
3285
3286 deselectFields();
3287
3288 sizeOfFieldGroup = getSizeOfFieldGroupFromChildElement( hoverTarget.querySelector( 'li.form-field' ) );
3289
3290 hoverTarget.classList.add( 'frm-has-open-field-group-popup' );
3291 jQuery( document ).on( 'click', '#frm_builder_page', destroyFieldGroupPopupOnOutsideClick );
3292
3293 popupWrapper = div();
3294 popupWrapper.style.position = 'relative';
3295 popupWrapper.appendChild( getFieldGroupPopup( sizeOfFieldGroup, this ) );
3296 this.parentNode.appendChild( popupWrapper );
3297
3298 const firstLayoutOption = popupWrapper.querySelector( '.frm-row-layout-option' );
3299 if ( firstLayoutOption ) {
3300 firstLayoutOption.focus();
3301 }
3302 }
3303
3304 function destroyFieldGroupPopupOnOutsideClick( event ) {
3305 if ( event.target.classList.contains( 'frm-custom-field-group-layout' ) || event.target.classList.contains( 'frm-cancel-custom-field-group-layout' ) ) {
3306 return;
3307 }
3308 if ( ! jQuery( event.target ).closest( '#frm_field_group_controls' ).length && ! jQuery( event.target ).closest( '#frm_field_group_popup' ).length ) {
3309 destroyFieldGroupPopup();
3310 }
3311 }
3312
3313 function getSizeOfFieldGroupFromChildElement( element ) {
3314 var $ul = jQuery( element ).closest( 'ul' );
3315 if ( $ul.length ) {
3316 return getFieldsInRow( $ul ).length;
3317 }
3318 return getSelectedFieldCount();
3319 }
3320
3321 function getFieldGroupPopup( sizeOfFieldGroup, childElement ) {
3322 var popup, wrapper, rowLayoutOptions, ul;
3323
3324 popup = document.getElementById( 'frm_field_group_popup' );
3325 if ( null === popup ) {
3326 popup = div();
3327 } else {
3328 popup.innerHTML = '';
3329 }
3330
3331 popup.id = 'frm_field_group_popup';
3332
3333 wrapper = div();
3334 wrapper.style.padding = '0 24px 12px';
3335 wrapper.appendChild( getRowLayoutTitle() );
3336
3337 rowLayoutOptions = getRowLayoutOptions( sizeOfFieldGroup );
3338
3339 ul = childElement.closest( 'ul.frm_sorting' );
3340 if ( null !== ul ) {
3341 maybeMarkRowLayoutAsActive( ul, rowLayoutOptions );
3342 }
3343
3344 wrapper.appendChild( rowLayoutOptions );
3345
3346 popup.appendChild( wrapper );
3347 popup.appendChild( separator() );
3348
3349 popup.appendChild( getCustomLayoutOption() );
3350 popup.appendChild( getBreakIntoDifferentRowsOption() );
3351
3352 return popup;
3353 }
3354
3355 function maybeMarkRowLayoutAsActive( activeRow, options ) {
3356 var length, index, currentRow;
3357
3358 length = options.children.length;
3359 for ( index = 0; index < length; ++index ) {
3360 currentRow = options.children[ index ];
3361 if ( rowLayoutsMatch( currentRow, activeRow ) ) {
3362 currentRow.classList.add( 'frm-active-row-layout' );
3363 return;
3364 }
3365 }
3366 }
3367
3368 function separator() {
3369 var hr = document.createElement( 'hr' );
3370 return hr;
3371 }
3372
3373 function getCustomLayoutOption() {
3374 var option = div();
3375 option.textContent = __( 'Custom layout', 'formidable' );
3376 jQuery( option ).prepend( getIconClone( 'frm_gear_svg' ) );
3377 option.classList.add( 'frm-custom-field-group-layout' );
3378 makeTabbable( option );
3379 return option;
3380 }
3381
3382 function makeTabbable( element, ariaLabel ) {
3383 element.setAttribute( 'tabindex', 0 );
3384 element.setAttribute( 'role', 'button' );
3385 if ( 'undefined' !== typeof ariaLabel ) {
3386 element.setAttribute( 'aria-label', ariaLabel );
3387 }
3388 }
3389
3390 function getIconClone( iconId ) {
3391 var clone = document.getElementById( iconId ).cloneNode( true );
3392 clone.id = '';
3393 return clone;
3394 }
3395
3396 function getBreakIntoDifferentRowsOption() {
3397 var option = div();
3398 option.textContent = __( 'Break into rows', 'formidable' );
3399 jQuery( option ).prepend( getIconClone( 'frm_break_field_group_svg' ) );
3400 option.classList.add( 'frm-break-field-group' );
3401 makeTabbable( option );
3402 return option;
3403 }
3404
3405 function getRowLayoutTitle() {
3406 var rowLayoutTitle = div();
3407 rowLayoutTitle.classList.add( 'frm-row-layout-title' );
3408 rowLayoutTitle.textContent = __( 'Row Layout', 'formidable' );
3409 return rowLayoutTitle;
3410 }
3411
3412 function getRowLayoutOptions( size ) {
3413 var wrapper, padding;
3414
3415 wrapper = getEmptyGridContainer();
3416 if ( 5 !== size ) {
3417 wrapper.appendChild( getRowLayoutOption( size, 'even' ) );
3418 }
3419 if ( size % 2 === 1 ) {
3420 // only include the middle option for odd numbers because even doesn't make a lot of sense.
3421 wrapper.appendChild( getRowLayoutOption( size, 'middle' ) );
3422 }
3423 if ( size < 6 ) {
3424 wrapper.appendChild( getRowLayoutOption( size, 'left' ) );
3425 wrapper.appendChild( getRowLayoutOption( size, 'right' ) );
3426 } else {
3427 padding = div();
3428 padding.classList.add( 'frm_fourth' );
3429 wrapper.prepend( padding );
3430 }
3431
3432 return wrapper;
3433 }
3434
3435 function getRowLayoutOption( size, type ) {
3436 var option, useClass;
3437
3438 option = div();
3439 option.classList.add( 'frm-row-layout-option' );
3440 makeTabbable( option, type );
3441
3442 switch ( size ) {
3443 case 6:
3444 useClass = 'frm_half';
3445 break;
3446 case 5:
3447 useClass = 'frm_third';
3448 break;
3449 default:
3450 useClass = size % 2 === 1 ? 'frm_fourth' : 'frm_third';
3451 break;
3452 }
3453
3454 option.classList.add( useClass );
3455 option.setAttribute( 'layout-type', type );
3456
3457 option.appendChild( getRowForSizeAndType( size, type ) );
3458 return option;
3459 }
3460
3461 function rowLayoutsMatch( row1, row2 ) {
3462 return getRowLayoutAsKey( row1 ) === getRowLayoutAsKey( row2 );
3463 }
3464
3465 function getRowLayoutAsKey( row ) {
3466 var $fields, sizes;
3467 if ( row.classList.contains( 'frm-row-layout-option' ) ) {
3468 $fields = jQuery( row ).find( '.frm_grid_container' ).children();
3469 } else {
3470 $fields = getFieldsInRow( jQuery( row ) );
3471 }
3472 sizes = [];
3473 $fields.each(
3474 function() {
3475 sizes.push( getSizeOfLayoutClass( getLayoutClassName( this.classList ) ) );
3476 }
3477 );
3478 return sizes.join( '-' );
3479 }
3480
3481 function getRowForSizeAndType( size, type ) {
3482 var row, index, block;
3483
3484 row = getEmptyGridContainer();
3485 for ( index = 0; index < size; ++index ) {
3486 block = div();
3487 block.classList.add( getClassForBlock( size, type, index ) );
3488 block.style.height = '16px';
3489 block.style.background = '#9EA9B8';
3490 block.style.borderRadius = '1px';
3491 row.appendChild( block );
3492 }
3493
3494 return row;
3495 }
3496
3497 /**
3498 * @param {int} size 2-6.
3499 * @param {string} type even, middle, left, or right.
3500 * @param {int} index 0-5.
3501 * @returns string
3502 */
3503 function getClassForBlock( size, type, index ) {
3504 if ( 'even' === type ) {
3505 return getEvenClassForSize( size, index );
3506 } else if ( 'middle' === type ) {
3507 if ( 3 === size ) {
3508 return 1 === index ? 'frm6' : 'frm3';
3509 }
3510 if ( 5 === size ) {
3511 return 2 === index ? 'frm4' : 'frm2';
3512 }
3513 } else if ( 'left' === type ) {
3514 return 0 === index ? getLargeClassForSize( size ) : getSmallClassForSize( size );
3515 } else if ( 'right' === type ) {
3516 return index === size - 1 ? getLargeClassForSize( size ) : getSmallClassForSize( size );
3517 }
3518 return 'frm12';
3519 }
3520
3521 function getEvenClassForSize( size, index ) {
3522 if ( -1 !== [ 2, 3, 4, 6 ].indexOf( size ) ) {
3523 return getLayoutClassForSize( 12 / size );
3524 }
3525 if ( 5 === size && 'undefined' !== typeof index ) {
3526 return 0 === index ? 'frm4' : 'frm2';
3527 }
3528 return 'frm12';
3529 }
3530
3531 function getSmallClassForSize( size ) {
3532 switch ( size ) {
3533 case 2: case 3:
3534 return 'frm3';
3535 case 4:
3536 return 'frm2';
3537 case 5:
3538 return 'frm2';
3539 case 6:
3540 return 'frm1';
3541 }
3542 return 'frm12';
3543 }
3544
3545 function getLargeClassForSize( size ) {
3546 switch ( size ) {
3547 case 2:
3548 return 'frm9';
3549 case 3: case 4:
3550 return 'frm6';
3551 case 5:
3552 return 'frm4';
3553 case 6:
3554 return 'frm7';
3555 }
3556 return 'frm12';
3557 }
3558
3559 function getEmptyGridContainer() {
3560 var wrapper = div();
3561 wrapper.classList.add( 'frm_grid_container' );
3562 return wrapper;
3563 }
3564
3565 function handleFieldGroupLayoutOptionClick() {
3566 var type, row;
3567 type = this.getAttribute( 'layout-type' );
3568 row = document.querySelector( '.frm-field-group-hover-target' );
3569 size = getFieldsInRow( jQuery( row ) ).length;
3570 syncLayoutClasses( getFieldsInRow( jQuery( row ) ).first(), type );
3571 destroyFieldGroupPopup();
3572 }
3573
3574 function handleFieldGroupLayoutOptionInsideMergeClick() {
3575 var $ul, type;
3576 $ul = mergeSelectedFieldGroups();
3577 type = this.getAttribute( 'layout-type' );
3578 syncLayoutClasses( getFieldsInRow( $ul ).first(), type );
3579 unselectFieldGroups();
3580 }
3581
3582 function mergeSelectedFieldGroups() {
3583 var $selectedFieldGroups = jQuery( '.frm-selected-field-group' ),
3584 $firstGroupUl = $selectedFieldGroups.first();
3585 $selectedFieldGroups.not( $firstGroupUl ).each(
3586 function() {
3587 getFieldsInRow( jQuery( this ) ).each(
3588 function() {
3589 var previousParent = this.parentNode;
3590 getFieldsInRow( $firstGroupUl ).last().after( this );
3591 if ( ! jQuery( previousParent ).children( 'li.form-field' ).length ) {
3592 // clean up the previous field group if we've removed all of its fields.
3593 previousParent.closest( 'li.frm_field_box' ).remove();
3594 }
3595 }
3596 );
3597 }
3598 );
3599 updateFieldOrder();
3600 syncLayoutClasses( getFieldsInRow( $firstGroupUl ).first() );
3601 return $firstGroupUl;
3602 }
3603
3604 function customFieldGroupLayoutClick() {
3605 var $fields;
3606 if ( null !== this.closest( '.frm-merge-fields-into-row' ) ) {
3607 return;
3608 }
3609 $fields = getFieldsInRow( jQuery( '.frm-field-group-hover-target' ) );
3610 setupCustomLayoutOptions( $fields );
3611 }
3612
3613 function setupCustomLayoutOptions( $fields ) {
3614 var size, popup, wrapper, layoutClass, inputRow, paddingElement, inputValueOverride, index, inputField, heading, label, buttonsWrapper, cancelButton, saveButton;
3615
3616 size = $fields.length;
3617
3618 popup = document.getElementById( 'frm_field_group_popup' );
3619 popup.innerHTML = '';
3620
3621 wrapper = div();
3622 wrapper.style.padding = '24px';
3623
3624 layoutClass = getEvenClassForSize( 5 === size ? 6 : size );
3625
3626 inputRow = div();
3627 inputRow.style.padding = '20px 0';
3628 inputRow.classList.add( 'frm_grid_container' );
3629
3630 if ( 5 === size ) {
3631 // add a span to pad the inputs by 1 column, to account for the missing 2 columns.
3632 paddingElement = document.createElement( 'span' );
3633 paddingElement.classList.add( 'frm1' );
3634 inputRow.appendChild( paddingElement );
3635 }
3636
3637 inputValueOverride = getSelectedFieldCount() > 0 ? getSizeOfLayoutClass( getEvenClassForSize( size ) ) : false;
3638 if ( false !== inputValueOverride && inputValueOverride >= 12 ) {
3639 inputValueOverride = Math.floor( 12 / size );
3640 }
3641
3642 for ( index = 0; index < size; ++index ) {
3643 inputField = document.createElement( 'input' );
3644 inputField.type = 'text';
3645 inputField.classList.add( layoutClass );
3646 inputField.classList.add( 'frm-custom-grid-size-input' );
3647 inputField.value = false !== inputValueOverride ? inputValueOverride : getSizeOfLayoutClass( getLayoutClassName( $fields.get( index ).classList ) );
3648 inputRow.appendChild( inputField );
3649 }
3650
3651 heading = div();
3652 heading.classList.add( 'frm-builder-popup-heading' );
3653 heading.textContent = __( 'Enter number of columns for each field', 'formidable' );
3654
3655 label = div();
3656 label.classList.add( 'frm-builder-popup-subheading' );
3657 label.textContent = __( 'Layouts are based on a 12-column grid system', 'formidable' );
3658
3659 wrapper.appendChild( heading );
3660 wrapper.appendChild( label );
3661
3662 wrapper.appendChild( inputRow );
3663
3664 buttonsWrapper = div();
3665 buttonsWrapper.style.textAlign = 'right';
3666
3667 cancelButton = getSecondaryButton();
3668 cancelButton.textContent = __( 'Cancel', 'formidable' );
3669 cancelButton.classList.add( 'frm-cancel-custom-field-group-layout' );
3670 cancelButton.style.marginRight = '10px';
3671
3672 saveButton = getPrimaryButton();
3673 saveButton.textContent = __( 'Save', 'formidable' );
3674 saveButton.classList.add( 'frm-save-custom-field-group-layout' );
3675
3676 buttonsWrapper.appendChild( cancelButton );
3677 buttonsWrapper.appendChild( saveButton );
3678
3679 wrapper.appendChild( buttonsWrapper );
3680
3681 popup.appendChild( wrapper );
3682
3683 setTimeout(
3684 function() {
3685 const firstInput = popup.querySelector( 'input.frm-custom-grid-size-input' ).focus();
3686 if ( firstInput ) {
3687 firstInput.focus();
3688 }
3689 },
3690 0
3691 );
3692 }
3693
3694 function customFieldGroupLayoutInsideMergeClick() {
3695 $fields = jQuery( '.frm-selected-field-group li.form-field' );
3696 setupCustomLayoutOptions( $fields );
3697 }
3698
3699 function getPrimaryButton() {
3700 var button = getButton();
3701 button.classList.add( 'button-primary', 'frm-button-primary' );
3702 return button;
3703 }
3704
3705 function getSecondaryButton() {
3706 var button = getButton();
3707 button.classList.add( 'button-secondary', 'frm-button-secondary' );
3708 return button;
3709 }
3710
3711 function getButton() {
3712 var button = document.createElement( 'a' );
3713 button.setAttribute( 'href', '#' );
3714 button.classList.add( 'button' );
3715 button.style.textDecoration = 'none';
3716 return button;
3717 }
3718
3719 function getSizeOfLayoutClass( className ) {
3720 switch ( className ) {
3721 case 'frm_half':
3722 return 6;
3723 case 'frm_third':
3724 return 4;
3725 case 'frm_two_thirds':
3726 return 8;
3727 case 'frm_fourth':
3728 return 3;
3729 case 'frm_three_fourths':
3730 return 9;
3731 case 'frm_sixth':
3732 return 2;
3733 }
3734
3735 if ( 0 === className.indexOf( 'frm' ) ) {
3736 return parseInt( className.substr( 3 ) );
3737 }
3738
3739 // Anything missing a layout class should be a full width row.
3740 return 12;
3741 }
3742
3743 function getLayoutClassName( classList ) {
3744 var classes, index, currentClass;
3745 classes = getLayoutClasses();
3746 for ( index = 0; index < classes.length; ++index ) {
3747 currentClass = classes[ index ];
3748 if ( classList.contains( currentClass ) ) {
3749 return currentClass;
3750 }
3751 }
3752 return '';
3753 }
3754
3755 function getLayoutClassForSize( size ) {
3756 return 'frm' + size;
3757 }
3758
3759 function breakFieldGroupClick() {
3760 var row = document.querySelector( '.frm-field-group-hover-target' );
3761 breakRow( row );
3762 destroyFieldGroupPopup();
3763 }
3764
3765 function breakRow( row ) {
3766 getFieldsInRow( jQuery( row ) ).each(
3767 function( index ) {
3768 if ( 0 !== index ) {
3769 jQuery( row ).closest( 'li' ).after( wrapFieldLi( this ) );
3770 }
3771 stripLayoutFromFields( jQuery( this ) );
3772 }
3773 );
3774 }
3775
3776 function stripLayoutFromFields( field ) {
3777 syncLayoutClasses( field, 'clear' );
3778 }
3779
3780 function focusFieldGroupInputOnClick() {
3781 this.select();
3782 }
3783
3784 function cancelCustomFieldGroupClick() {
3785 revertToFieldGroupPopupFirstPage( this );
3786 }
3787
3788 function revertToFieldGroupPopupFirstPage( triggerElement ) {
3789 jQuery( document.getElementById( 'frm_field_group_popup' ) ).replaceWith(
3790 getFieldGroupPopup( getSizeOfFieldGroupFromChildElement( triggerElement ), triggerElement )
3791 );
3792 }
3793
3794 function destroyFieldGroupPopup() {
3795 var popup, wrapper;
3796 popup = document.getElementById( 'frm_field_group_popup' );
3797 if ( popup === null ) {
3798 return;
3799 }
3800 wrapper = document.querySelector( '.frm-has-open-field-group-popup' );
3801 if ( null !== wrapper ) {
3802 wrapper.classList.remove( 'frm-has-open-field-group-popup' );
3803 popup.parentNode.remove();
3804 }
3805 jQuery( document ).off( 'click', '#frm_builder_page', destroyFieldGroupPopupOnOutsideClick );
3806 }
3807
3808 function saveCustomFieldGroupClick() {
3809 var syncDetails, $controls, $ul;
3810
3811 syncDetails = [];
3812
3813 jQuery( document.getElementById( 'frm_field_group_popup' ).querySelectorAll( '.frm_grid_container input' ) )
3814 .each(
3815 function() {
3816 syncDetails.push( parseInt( this.value ) );
3817 }
3818 );
3819
3820 $controls = jQuery( document.getElementById( 'frm_field_group_controls' ) );
3821
3822 if ( $controls.length && 'none' !== $controls.get( 0 ).style.display ) {
3823 syncLayoutClasses( getFieldsInRow( jQuery( document.querySelector( '.frm-field-group-hover-target' ) ) ).first(), syncDetails );
3824 } else {
3825 $ul = mergeSelectedFieldGroups();
3826 syncLayoutClasses( getFieldsInRow( $ul ).first(), syncDetails );
3827 unselectFieldGroups();
3828 }
3829
3830 destroyFieldGroupPopup();
3831 }
3832
3833 function fieldGroupClick( e ) {
3834 if ( 'ul' !== e.originalEvent.target.nodeName.toLowerCase() ) {
3835 // only continue if the group itself was clicked / ignore when a field is clicked.
3836 return;
3837 }
3838
3839 const hoverTarget = document.querySelector( '.frm-field-group-hover-target' );
3840 if ( ! hoverTarget ) {
3841 return;
3842 }
3843
3844 const ctrlOrCmdKeyIsDown = e.ctrlKey || e.metaKey;
3845 const shiftKeyIsDown = e.shiftKey;
3846 const groupIsActive = hoverTarget.classList.contains( 'frm-selected-field-group' );
3847 const $selectedFieldGroups = getSelectedFieldGroups();
3848
3849 let numberOfSelectedGroups = $selectedFieldGroups.length;
3850
3851 if ( ctrlOrCmdKeyIsDown || shiftKeyIsDown ) {
3852 // multi-selecting
3853
3854 const selectedField = getSelectedField();
3855 if ( null !== selectedField && ! jQuery( selectedField ).siblings( 'li.form-field' ).length ) {
3856 // count a selected field on its own as a selected field group when multiselecting.
3857 selectedField.parentNode.classList.add( 'frm-selected-field-group' );
3858 ++numberOfSelectedGroups;
3859 }
3860
3861 if ( ctrlOrCmdKeyIsDown ) {
3862 if ( groupIsActive ) {
3863 // unselect if holding ctrl or cmd and the group was already active.
3864 --numberOfSelectedGroups;
3865 hoverTarget.classList.remove( 'frm-selected-field-group' );
3866 syncAfterMultiSelect( numberOfSelectedGroups );
3867 return; // exit early to avoid adding back frm-selected-field-group
3868 } else {
3869 ++numberOfSelectedGroups;
3870 }
3871 } else if ( shiftKeyIsDown && ! groupIsActive ) {
3872 ++numberOfSelectedGroups; // include the one we're selecting right now.
3873 const $firstGroup = $selectedFieldGroups.first();
3874
3875 let $range;
3876 if ( $firstGroup.parent().index() < jQuery( hoverTarget.parentNode ).index() ) {
3877 $range = $firstGroup.parent().nextUntil( hoverTarget.parentNode );
3878 } else {
3879 $range = $firstGroup.parent().prevUntil( hoverTarget.parentNode );
3880 }
3881
3882 $range.each(
3883 function() {
3884 var $fieldGroup = jQuery( this ).closest( 'li' ).find( 'ul.frm_sorting' );
3885 if ( ! $fieldGroup.hasClass( 'frm-selected-field-group' ) ) {
3886 ++numberOfSelectedGroups;
3887 $fieldGroup.addClass( 'frm-selected-field-group' );
3888 }
3889 }
3890 );
3891
3892 // when holding shift and clicking, text gets selected. unselect it.
3893 document.getSelection().removeAllRanges();
3894 }
3895 } else {
3896 // not multi-selecting
3897 unselectFieldGroups();
3898 numberOfSelectedGroups = 1;
3899 }
3900
3901 hoverTarget.classList.add( 'frm-selected-field-group' );
3902 syncAfterMultiSelect( numberOfSelectedGroups );
3903
3904 jQuery( document ).off( 'click', unselectFieldGroups );
3905 jQuery( document ).on( 'click', unselectFieldGroups );
3906 }
3907
3908 function getSelectedField() {
3909 return document.getElementById( 'frm-show-fields' ).querySelector( 'li.form-field.selected' );
3910 }
3911
3912 function getSelectedFieldGroups() {
3913 const $fieldGroups = jQuery( '.frm-selected-field-group' );
3914 if ( $fieldGroups.length ) {
3915 return $fieldGroups;
3916 }
3917
3918 const selectedField = getSelectedField();
3919 if ( selectedField ) {
3920 // If there is only one field in a group and the field is selected, consider the field's group as selected for multi-select.
3921 const selectedFieldGroup = selectedField.closest( 'ul' );
3922 if ( selectedFieldGroup && 1 === getFieldsInRow( jQuery( selectedFieldGroup ) ).length ) {
3923 selectedFieldGroup.classList.add( 'frm-selected-field-group' );
3924 return jQuery( selectedFieldGroup );
3925 }
3926 }
3927
3928 return jQuery();
3929 }
3930
3931 function syncAfterMultiSelect( numberOfSelectedGroups ) {
3932 clearSettingsBox( true ); // unselect any fields if one is selected.
3933 if ( numberOfSelectedGroups >= 2 || ( 1 === numberOfSelectedGroups && selectedGroupHasMultipleFields() ) ) {
3934 addFieldMultiselectPopup();
3935 } else {
3936 maybeRemoveMultiselectPopup();
3937 }
3938 maybeRemoveGroupHoverTarget();
3939 }
3940
3941 function selectedGroupHasMultipleFields() {
3942 return getFieldsInRow( jQuery( document.querySelector( '.frm-selected-field-group' ) ) ).length > 1;
3943 }
3944
3945 function unselectFieldGroups( event ) {
3946 if ( 'undefined' !== typeof event ) {
3947 if ( null !== event.originalEvent.target.closest( '#frm-show-fields' ) ) {
3948 return;
3949 }
3950 if ( event.originalEvent.target.classList.contains( 'frm-merge-fields-into-row' ) ) {
3951 return;
3952 }
3953 if ( null !== event.originalEvent.target.closest( '.frm-merge-fields-into-row' ) ) {
3954 return;
3955 }
3956 if ( event.originalEvent.target.classList.contains( 'frm-custom-field-group-layout' ) ) {
3957 return;
3958 }
3959 if ( event.originalEvent.target.classList.contains( 'frm-cancel-custom-field-group-layout' ) ) {
3960 return;
3961 }
3962 }
3963 jQuery( '.frm-selected-field-group' ).removeClass( 'frm-selected-field-group' );
3964 jQuery( document ).off( 'click', unselectFieldGroups );
3965 maybeRemoveMultiselectPopup();
3966 }
3967
3968 function maybeRemoveMultiselectPopup() {
3969 var popup = document.getElementById( 'frm_field_multiselect_popup' );
3970 if ( null !== popup ) {
3971 popup.remove();
3972 }
3973 }
3974
3975 function addFieldMultiselectPopup() {
3976 getFieldMultiselectPopup();
3977 }
3978
3979 function getFieldMultiselectPopup() {
3980 var popup, mergeOption, caret, verticalSeparator, deleteOption;
3981
3982 popup = document.getElementById( 'frm_field_multiselect_popup' );
3983
3984 if ( null !== popup ) {
3985 popup.classList.toggle( 'frm-unmergable', ! selectedFieldsAreMergable() );
3986 return popup;
3987 }
3988
3989 popup = div();
3990 popup.id = 'frm_field_multiselect_popup';
3991 if ( ! selectedFieldsAreMergable() ) {
3992 popup.classList.add( 'frm-unmergable' );
3993 }
3994
3995 mergeOption = div();
3996 mergeOption.classList.add( 'frm-merge-fields-into-row' );
3997 mergeOption.textContent = __( 'Merge into row', 'formidable' );
3998
3999 caret = document.createElement( 'a' );
4000 caret.style.marginLeft = '5px';
4001 caret.classList.add( 'frm_icon_font', 'frm_arrowdown6_icon' );
4002 caret.setAttribute( 'href', '#' );
4003 mergeOption.appendChild( caret );
4004
4005 popup.appendChild( mergeOption );
4006
4007 verticalSeparator = div();
4008 verticalSeparator.classList.add( 'frm-multiselect-popup-separator' );
4009 popup.appendChild( verticalSeparator );
4010
4011 deleteOption = div();
4012 deleteOption.classList.add( 'frm-delete-field-groups' );
4013 deleteOption.appendChild( getIconClone( 'frm_trash_svg' ) );
4014 popup.appendChild( deleteOption );
4015
4016 document.getElementById( 'post-body-content' ).appendChild( popup );
4017
4018 jQuery( popup ).hide().fadeIn();
4019
4020 return popup;
4021 }
4022
4023 function selectedFieldsAreMergable() {
4024 var selectedFieldGroups, totalFieldCount, length, index, fieldGroup;
4025 selectedFieldGroups = document.querySelectorAll( '.frm-selected-field-group' );
4026 length = selectedFieldGroups.length;
4027 if ( 1 === length ) {
4028 return false;
4029 }
4030 totalFieldCount = 0;
4031 for ( index = 0; index < length; ++index ) {
4032 fieldGroup = selectedFieldGroups[ index ];
4033 if ( null !== fieldGroup.querySelector( '.edit_field_type_break, .edit_field_type_hidden' ) ) {
4034 return false;
4035 }
4036 totalFieldCount += getFieldsInRow( jQuery( fieldGroup ) ).length;
4037 if ( totalFieldCount > 6 ) {
4038 return false;
4039 }
4040 }
4041 return true;
4042 }
4043
4044 function mergeFieldsIntoRowClick( event ) {
4045 var size, popup;
4046
4047 if ( null !== event.originalEvent.target.closest( '#frm_field_group_popup' ) ) {
4048 // prevent clicks within the popup from triggering the button again.
4049 return;
4050 }
4051
4052 if ( event.originalEvent.target.classList.contains( 'frm-custom-field-group-layout' ) ) {
4053 // avoid switching back to the first page when clicking the custom option nested inside of the merge option.
4054 return;
4055 }
4056
4057 size = getSelectedFieldCount();
4058 popup = getFieldGroupPopup( size, document.querySelector( '.frm-selected-field-group' ).firstChild );
4059 this.appendChild( popup );
4060 }
4061
4062 function getSelectedFieldCount() {
4063 var count = 0;
4064 jQuery( document.querySelectorAll( '.frm-selected-field-group' ) ).each(
4065 function() {
4066 count += getFieldsInRow( jQuery( this ) ).length;
4067 }
4068 );
4069 return count;
4070 }
4071
4072 function deleteFieldGroupsClick() {
4073 var fieldIdsToDelete, deleteOnConfirm, multiselectPopup;
4074
4075 fieldIdsToDelete = getSelectedFieldIds();
4076 deleteOnConfirm = getDeleteSelectedFieldGroupsOnConfirmFunction( fieldIdsToDelete );
4077
4078 multiselectPopup = document.getElementById( 'frm_field_multiselect_popup' );
4079 if ( null !== multiselectPopup ) {
4080 multiselectPopup.remove();
4081 }
4082
4083 this.setAttribute( 'data-frmcaution', __( 'Heads up', 'formidable' ) );
4084 /* translators: %1$s: Number of fields that are selected to be deleted. */
4085 this.setAttribute( 'data-frmverify', __( 'Are you sure you want to delete these %1$s selected fields?', 'formidable' ).replace( '%1$s', fieldIdsToDelete.length ) );
4086 confirmLinkClick( this );
4087
4088 jQuery( '#frm-confirmed-click' ).on( 'click', deleteOnConfirm );
4089 jQuery( '#frm_confirm_modal' ).one( 'dialogclose', function() {
4090 jQuery( '#frm-confirmed-click' ).off( 'click', deleteOnConfirm );
4091 });
4092 }
4093
4094 function getSelectedFieldIds() {
4095 var deleteFieldIds = [];
4096 jQuery( '.frm-selected-field-group > li.form-field' )
4097 .not( '.ui-sortable-helper' )
4098 .each(
4099 function() {
4100 deleteFieldIds.push( this.dataset.fid );
4101 }
4102 );
4103 return deleteFieldIds;
4104 }
4105
4106 function getDeleteSelectedFieldGroupsOnConfirmFunction( deleteFieldIds ) {
4107 return function( event ) {
4108 event.preventDefault();
4109 deleteAllSelectedFieldGroups( deleteFieldIds );
4110 };
4111 }
4112
4113 function deleteAllSelectedFieldGroups( deleteFieldIds ) {
4114 deleteFieldIds.forEach(
4115 function( fieldId ) {
4116 deleteField( fieldId );
4117 }
4118 );
4119 }
4120
4121 function deleteFieldConfirmed() {
4122 /*jshint validthis:true */
4123 deleteFields( this.getAttribute( 'data-deletefield' ) );
4124 }
4125
4126 function deleteFields( fieldId ) {
4127 var field = jQuery( '#frm_field_id_' + fieldId );
4128
4129 deleteField( fieldId );
4130
4131 if ( field.hasClass( 'edit_field_type_divider' ) ) {
4132 field.find( 'li.frm_field_box' ).each( function() {
4133 //TODO: maybe delete only end section
4134 //if(n.hasClass('edit_field_type_end_divider')){
4135 deleteField( this.getAttribute( 'data-fid' ) );
4136 //}
4137 });
4138 }
4139 toggleSectionHolder();
4140 }
4141
4142 function deleteField( fieldId ) {
4143 jQuery.ajax({
4144 type: 'POST',
4145 url: ajaxurl,
4146 data: {
4147 action: 'frm_delete_field',
4148 field_id: fieldId,
4149 nonce: frmGlobal.nonce
4150 },
4151 success: function() {
4152 var $thisField = jQuery( document.getElementById( 'frm_field_id_' + fieldId ) ),
4153 settings = jQuery( '#frm-single-settings-' + fieldId );
4154
4155 // Remove settings from sidebar.
4156 if ( settings.is( ':visible' ) ) {
4157 document.getElementById( 'frm_insert_fields_tab' ).click();
4158 }
4159 settings.remove();
4160
4161 $thisField.fadeOut( 'slow', function() {
4162 var $section = $thisField.closest( '.start_divider' ),
4163 type = $thisField.data( 'type' ),
4164 $adjacentFields = $thisField.siblings( 'li.form-field' ),
4165 $liWrapper;
4166
4167 if ( ! $adjacentFields.length ) {
4168 if ( $thisField.is( '.edit_field_type_end_divider' ) ) {
4169 $adjacentFields.length = $thisField.closest( 'li.form-field' ).siblings();
4170 } else {
4171 $liWrapper = $thisField.closest( 'ul.frm_sorting' ).parent();
4172 }
4173 }
4174
4175 $thisField.remove();
4176 if ( type === 'break' ) {
4177 renumberPageBreaks();
4178 }
4179 if ( type === 'product' ) {
4180 maybeHideQuantityProductFieldOption();
4181 // a product field attached to a quantity field earlier might be the one deleted, so re-populate
4182 popAllProductFields();
4183 }
4184 if ( jQuery( '#frm-show-fields li' ).length === 0 ) {
4185 document.getElementById( 'frm_form_editor_container' ).classList.remove( 'frm-has-fields' );
4186 } else if ( $section.length ) {
4187 toggleOneSectionHolder( $section );
4188 }
4189 if ( $adjacentFields.length ) {
4190 syncLayoutClasses( $adjacentFields.first() );
4191 } else {
4192 $liWrapper.remove();
4193 }
4194
4195 // prevent "More Options" tooltips from staying around after their target field is deleted.
4196 deleteTooltips();
4197 });
4198 }
4199 });
4200 }
4201
4202 function addFieldLogicRow() {
4203 /*jshint validthis:true */
4204 var id = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' ),
4205 formId = thisFormId,
4206 logicRows = document.getElementById( 'frm_logic_row_' + id ).querySelectorAll( '.frm_logic_row' );
4207 jQuery.ajax({
4208 type: 'POST',
4209 url: ajaxurl,
4210 data: {
4211 action: 'frm_add_logic_row',
4212 form_id: formId,
4213 field_id: id,
4214 nonce: frmGlobal.nonce,
4215 meta_name: getNewRowId( logicRows, 'frm_logic_' + id + '_' ),
4216 fields: getFieldList()
4217 },
4218 success: function( html ) {
4219 jQuery( document.getElementById( 'logic_' + id ) ).fadeOut( 'slow', function() {
4220 var logicRow = jQuery( document.getElementById( 'frm_logic_row_' + id ) );
4221 logicRow.append( html );
4222 logicRow.closest( '.frm_logic_rows' ).fadeIn( 'slow' );
4223 });
4224 }
4225 });
4226 return false;
4227 }
4228
4229 function getNewRowId( rows, replace, defaultValue ) {
4230 if ( ! rows.length ) {
4231 return 'undefined' !== typeof defaultValue ? defaultValue : 0;
4232 }
4233 return parseInt( rows[ rows.length - 1 ].id.replace( replace, '' ), 10 ) + 1;
4234 }
4235
4236 function addWatchLookupRow() {
4237 /*jshint validthis:true */
4238 var lastRowId,
4239 id = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' ),
4240 formId = thisFormId,
4241 lookupBlockRows = document.getElementById( 'frm_watch_lookup_block_' + id ).children;
4242 jQuery.ajax({
4243 type: 'POST',
4244 url: ajaxurl,
4245 data: {
4246 action: 'frm_add_watch_lookup_row',
4247 form_id: formId,
4248 field_id: id,
4249 row_key: getNewRowId( lookupBlockRows, 'frm_watch_lookup_' + id + '_' ),
4250 nonce: frmGlobal.nonce
4251 },
4252 success: function( newRow ) {
4253 var watchRowBlock = jQuery( document.getElementById( 'frm_watch_lookup_block_' + id ) );
4254 watchRowBlock.append( newRow );
4255 watchRowBlock.fadeIn( 'slow' );
4256 }
4257 });
4258 return false;
4259 }
4260
4261 function resetOptionTextDetails() {
4262 jQuery( '.frm-single-settings ul input[type="text"][name^="field_options[options_"]' ).filter( '[data-value-on-load]' ).removeAttr( 'data-value-on-load' );
4263 jQuery( 'input[type="hidden"][name^=optionmap]' ).remove();
4264 }
4265
4266 function optionTextAlreadyExists( input ) {
4267 var fieldId = jQuery( input ).closest( '.frm-single-settings' ).attr( 'data-fid' ),
4268 optionInputs = jQuery( input ).closest( 'ul' ).get( 0 ).querySelectorAll( '.field_' + fieldId + '_option' ),
4269 index,
4270 optionInput;
4271
4272 for ( index in optionInputs ) {
4273 optionInput = optionInputs[ index ];
4274 if ( optionInput.id !== input.id && optionInput.value === input.value && optionInput.getAttribute( 'data-duplicate' ) !== 'true' ) {
4275 return true;
4276 }
4277 }
4278
4279 return false;
4280 }
4281
4282 function onOptionTextFocus() {
4283 var input,
4284 fieldId;
4285
4286 if ( this.getAttribute( 'data-value-on-load' ) === null ) {
4287 this.setAttribute( 'data-value-on-load', this.value );
4288
4289 fieldId = jQuery( this ).closest( '.frm-single-settings' ).attr( 'data-fid' );
4290 input = document.createElement( 'input' );
4291 input.value = this.value;
4292 input.setAttribute( 'type', 'hidden' );
4293 input.setAttribute( 'name', 'optionmap[' + fieldId + '][' + this.value + ']' );
4294 this.parentNode.appendChild( input );
4295
4296 if ( typeof optionMap[ fieldId ] === 'undefined' ) {
4297 optionMap[ fieldId ] = {};
4298 }
4299
4300 optionMap[ fieldId ][ this.value ] = input;
4301 }
4302
4303 if ( this.getAttribute( 'data-duplicate' ) === 'true' ) {
4304 this.removeAttribute( 'data-duplicate' );
4305
4306 // we want to use original value if actually still a duplicate
4307 if ( optionTextAlreadyExists( this ) ) {
4308 this.setAttribute( 'data-value-on-focus', this.getAttribute( 'data-value-on-load' ) );
4309 return;
4310 }
4311 }
4312
4313 if ( '' !== this.value || frm_admin_js.new_option !== this.getAttribute( 'data-value-on-focus' ) ) {
4314 this.setAttribute( 'data-value-on-focus', this.value );
4315 }
4316 }
4317
4318 function onOptionTextBlur() {
4319 var originalValue,
4320 oldValue = this.getAttribute( 'data-value-on-focus' ),
4321 newValue = this.value,
4322 fieldId,
4323 fieldIndex,
4324 logicId,
4325 row,
4326 rowLength,
4327 rowIndex,
4328 valueSelect,
4329 opts,
4330 fieldIds,
4331 settingId,
4332 setting,
4333 optionMatches,
4334 option;
4335
4336 if ( oldValue === newValue ) {
4337 return;
4338 }
4339
4340 fieldId = jQuery( this ).closest( '.frm-single-settings' ).attr( 'data-fid' );
4341 originalValue = this.getAttribute( 'data-value-on-load' );
4342
4343 // check if the newValue is already mapped to another option
4344 // if it is, mark as duplicate and return
4345 if ( optionTextAlreadyExists( this ) ) {
4346 this.setAttribute( 'data-duplicate', 'true' );
4347
4348 if ( typeof optionMap[ fieldId ] !== 'undefined' && typeof optionMap[ fieldId ][ originalValue ] !== 'undefined' ) {
4349 // unmap any other change that may have happened before instead of changing it to something unused
4350 optionMap[ fieldId ][ originalValue ].value = originalValue;
4351 }
4352
4353 return;
4354 }
4355
4356 if ( typeof optionMap[ fieldId ] !== 'undefined' && typeof optionMap[ fieldId ][ originalValue ] !== 'undefined' ) {
4357 optionMap[ fieldId ][ originalValue ].value = newValue;
4358 }
4359
4360 fieldIds = [];
4361 rows = document.getElementById( 'frm_builder_page' ).querySelectorAll( '.frm_logic_row' );
4362 rowLength = rows.length;
4363 for ( rowIndex = 0; rowIndex < rowLength; rowIndex++ ) {
4364 row = rows[ rowIndex ];
4365 opts = row.querySelector( '.frm_logic_field_opts' );
4366
4367 if ( opts.value !== fieldId ) {
4368 continue;
4369 }
4370
4371 logicId = row.id.split( '_' )[ 2 ];
4372 valueSelect = row.querySelector( 'select[name="field_options[hide_opt_' + logicId + '][]"]' );
4373
4374 if ( '' === oldValue ) {
4375 optionMatches = [];
4376 } else {
4377 optionMatches = valueSelect.querySelectorAll( 'option[value="' + oldValue + '"]' );
4378 }
4379
4380 if ( ! optionMatches.length ) {
4381 optionMatches = valueSelect.querySelectorAll( 'option[value="' + newValue + '"]' );
4382
4383 if ( ! optionMatches.length ) {
4384 option = document.createElement( 'option' );
4385 valueSelect.appendChild( option );
4386 }
4387 }
4388
4389 if ( optionMatches.length ) {
4390 option = optionMatches[ optionMatches.length - 1 ];
4391 }
4392
4393 option.setAttribute( 'value', newValue );
4394 option.textContent = newValue;
4395
4396 if ( fieldIds.indexOf( logicId ) === -1 ) {
4397 fieldIds.push( logicId );
4398 }
4399 }
4400
4401 for ( fieldIndex in fieldIds ) {
4402 settingId = fieldIds[ fieldIndex ];
4403 setting = document.getElementById( 'frm-single-settings-' + settingId );
4404 moveFieldSettings( setting );
4405 }
4406 }
4407
4408 function updateGetValueFieldSelection() {
4409 /*jshint validthis:true */
4410 var fieldID = this.id.replace( 'get_values_form_', '' );
4411 var fieldSelect = document.getElementById( 'get_values_field_' + fieldID );
4412 var fieldType = this.getAttribute( 'data-fieldtype' );
4413
4414 if ( this.value === '' ) {
4415 fieldSelect.options.length = 1;
4416 } else {
4417 var formID = this.value;
4418 jQuery.ajax({
4419 type: 'POST', url: ajaxurl,
4420 data: {
4421 action: 'frm_get_options_for_get_values_field',
4422 form_id: formID,
4423 field_type: fieldType,
4424 nonce: frmGlobal.nonce
4425 },
4426 success: function( fields ) {
4427 fieldSelect.innerHTML = fields;
4428 }
4429 });
4430 }
4431 }
4432
4433 // Clear the Watch Fields option when Lookup field switches to "Text" option
4434 function maybeClearWatchFields() {
4435 /*jshint validthis:true */
4436 var link, lookupBlock,
4437 fieldID = this.name.replace( 'field_options[data_type_', '' ).replace( ']', '' );
4438
4439 link = document.getElementById( 'frm_add_watch_lookup_link_' + fieldID ).parentNode;
4440
4441 if ( this.value === 'text' ) {
4442 lookupBlock = document.getElementById( 'frm_watch_lookup_block_' + fieldID );
4443 if ( lookupBlock !== null ) {
4444 // Clear and hide the Watch Fields option
4445 lookupBlock.innerHTML = '';
4446 link.classList.add( 'frm_hidden' );
4447
4448 // Hide the Watch Fields row
4449 link.previousElementSibling.style.display = 'none';
4450 link.previousElementSibling.previousElementSibling.style.display = 'none';
4451 link.previousElementSibling.previousElementSibling.previousElementSibling.style.display = 'none';
4452 }
4453 } else {
4454 // Show the Watch Fields option
4455 link.classList.remove( 'frm_hidden' );
4456 }
4457
4458 toggleMultiSelect( fieldID, this.value );
4459 }
4460
4461 // Number the pages and hide/show the first page as needed.
4462 function renumberPageBreaks() {
4463 var i, containerClass,
4464 pages = document.getElementsByClassName( 'frm-page-num' );
4465
4466 if ( pages.length > 1 ) {
4467 document.getElementById( 'frm-fake-page' ).style.display = 'block';
4468 for ( i = 0; i < pages.length; i++ ) {
4469 containerClass = pages[i].parentNode.parentNode.parentNode.classList;
4470 if ( i === 1 ) {
4471 // Hide previous button on page 1
4472 containerClass.add( 'frm-first-page' );
4473 } else {
4474 containerClass.remove( 'frm-first-page' );
4475 }
4476 pages[i].innerHTML = ( i + 1 );
4477 }
4478 } else {
4479 document.getElementById( 'frm-fake-page' ).style.display = 'none';
4480 }
4481 }
4482
4483 // The fake field works differently than real fields.
4484 function maybeCollapsePage() {
4485 /*jshint validthis:true */
4486 var field = jQuery( this ).closest( '.frm_field_box[data-ftype=break]' );
4487 if ( field.length ) {
4488 toggleCollapsePage( field );
4489 } else {
4490 toggleCollapseFakePage();
4491 }
4492 }
4493
4494 // Find all fields in a page and hide/show them
4495 function toggleCollapsePage( field ) {
4496 var toCollapse = getAllFieldsForPage( field.get( 0 ).parentNode.closest( 'li.frm_field_box' ).nextElementSibling );
4497 togglePage( field, toCollapse );
4498 }
4499
4500 function toggleCollapseFakePage() {
4501 var topLevel = document.getElementById( 'frm-fake-page' ),
4502 firstField = document.getElementById( 'frm-show-fields' ).firstElementChild,
4503 toCollapse = getAllFieldsForPage( firstField );
4504
4505 if ( firstField.getAttribute( 'data-ftype' ) === 'break' ) {
4506 // Don't collapse if the first field is a page break.
4507 return;
4508 }
4509
4510 togglePage( jQuery( topLevel ), toCollapse );
4511 }
4512
4513 function getAllFieldsForPage( firstWrapper ) {
4514 var $fieldsForPage, currentWrapper;
4515
4516 $fieldsForPage = jQuery();
4517
4518 if ( null === firstWrapper ) {
4519 return $fieldsForPage;
4520 }
4521
4522 currentWrapper = firstWrapper;
4523
4524 do {
4525 if ( null !== currentWrapper.querySelector( '.edit_field_type_break' ) ) {
4526 break;
4527 }
4528 $fieldsForPage = $fieldsForPage.add( jQuery( currentWrapper ) );
4529 currentWrapper = currentWrapper.nextElementSibling;
4530 } while ( null !== currentWrapper );
4531
4532 return $fieldsForPage;
4533 }
4534
4535 function togglePage( field, toCollapse ) {
4536 var i,
4537 fieldCount = toCollapse.length,
4538 slide = Math.min( fieldCount, 3 );
4539
4540 if ( field.hasClass( 'frm-page-collapsed' ) ) {
4541 field.removeClass( 'frm-page-collapsed' );
4542 toCollapse.removeClass( 'frm-is-collapsed' );
4543 for ( i = 0; i < slide; i++ ) {
4544 if ( i === slide - 1 ) {
4545 jQuery( toCollapse[ i ]).slideDown( 150, function() {
4546 toCollapse.show();
4547 });
4548 } else {
4549 jQuery( toCollapse[ i ]).slideDown( 150 );
4550 }
4551 }
4552 } else {
4553 field.addClass( 'frm-page-collapsed' );
4554 toCollapse.addClass( 'frm-is-collapsed' );
4555 for ( i = 0; i < slide; i++ ) {
4556 if ( i === slide - 1 ) {
4557 jQuery( toCollapse[ i ]).slideUp( 150, function() {
4558 toCollapse.css( 'cssText', 'display:none !important;' );
4559 });
4560 } else {
4561 jQuery( toCollapse[ i ]).slideUp( 150 );
4562 }
4563 }
4564 }
4565 }
4566
4567 function maybeCollapseSection() {
4568 /*jshint validthis:true */
4569 var parentCont = this.parentNode.parentNode.parentNode.parentNode;
4570
4571 parentCont.classList.toggle( 'frm-section-collapsed' );
4572 }
4573
4574 function maybeCollapseSettings() {
4575 /*jshint validthis:true */
4576 this.classList.toggle( 'frm-collapsed' );
4577 }
4578
4579 function clickLabel() {
4580 if ( ! this.id ) {
4581 return;
4582 }
4583
4584 /*jshint validthis:true */
4585 var setting = document.querySelectorAll( '[data-changeme="' + this.id + '"]' )[0],
4586 fieldId = this.id.replace( 'field_label_', '' ),
4587 fieldType = document.getElementById( 'field_options_type_' + fieldId ),
4588 fieldTypeName = fieldType.value;
4589
4590 if ( typeof setting !== 'undefined' ) {
4591 if ( fieldType.tagName === 'SELECT' ) {
4592 fieldTypeName = fieldType.options[ fieldType.selectedIndex ].text.toLowerCase();
4593 } else {
4594 fieldTypeName = fieldTypeName.replace( '_', ' ' );
4595 }
4596
4597 fieldTypeName = normalizeFieldName( fieldTypeName );
4598
4599 setTimeout( function() {
4600 if ( setting.value.toLowerCase() === fieldTypeName ) {
4601 setting.select();
4602 } else {
4603 setting.focus();
4604 }
4605 }, 50 );
4606 }
4607 }
4608
4609 function clickDescription() {
4610 /*jshint validthis:true */
4611 var setting = document.querySelectorAll( '[data-changeme="' + this.id + '"]' )[0];
4612 if ( typeof setting !== 'undefined' ) {
4613 setTimeout( function() {
4614 setting.focus();
4615 autoExpandSettings( setting );
4616 }, 50 );
4617 }
4618 }
4619
4620 function autoExpandSettings( setting ) {
4621 var inSection = setting.closest( '.frm-collapse-me' );
4622 if ( inSection !== null ) {
4623 inSection.previousElementSibling.classList.remove( 'frm-collapsed' );
4624 }
4625 }
4626
4627 function normalizeFieldName( fieldTypeName ) {
4628 if ( fieldTypeName === 'divider' ) {
4629 fieldTypeName = 'section';
4630 } else if ( fieldTypeName === 'range' ) {
4631 fieldTypeName = 'slider';
4632 } else if ( fieldTypeName === 'data' ) {
4633 fieldTypeName = 'dynamic';
4634 } else if ( fieldTypeName === 'form' ) {
4635 fieldTypeName = 'embed form';
4636 }
4637 return fieldTypeName;
4638 }
4639
4640 function clickVis( e ) {
4641 /*jshint validthis:true */
4642 var currentClass, originalList;
4643
4644 currentClass = e.target.classList;
4645
4646 if ( currentClass.contains( 'frm-collapse-page' ) || currentClass.contains( 'frm-sub-label' ) || e.target.closest( '.dropdown' ) !== null ) {
4647 return;
4648 }
4649
4650 if ( this.closest( '.start_divider' ) !== null ) {
4651 e.stopPropagation();
4652 }
4653
4654 if ( this.classList.contains( 'edit_field_type_divider' ) ) {
4655 originalList = e.originalEvent.target.closest( 'ul.frm_sorting' );
4656 if ( null !== originalList ) {
4657 // prevent section click if clicking a field group within a section.
4658 if ( originalList.classList.contains( 'edit_field_type_divider' ) || originalList.parentNode.parentNode.classList.contains( 'start_divider' ) ) {
4659 return;
4660 }
4661 }
4662 }
4663
4664 clickAction( this );
4665 }
4666
4667 /**
4668 * Open Advanced settings on double click.
4669 */
4670 function openAdvanced() {
4671 var fieldId = this.getAttribute( 'data-fid' );
4672 autoExpandSettings( document.getElementById( 'field_options_field_key_' + fieldId ) );
4673 }
4674
4675 function toggleRepeatButtons() {
4676 /*jshint validthis:true */
4677 var $thisField = jQuery( this ).closest( '.frm_field_box' );
4678 $thisField.find( '.repeat_icon_links' ).removeClass( 'repeat_format repeat_formatboth repeat_formattext' ).addClass( 'repeat_format' + this.value );
4679 if ( this.value === 'text' || this.value === 'both' ) {
4680 $thisField.find( '.frm_repeat_text' ).show();
4681 $thisField.find( '.repeat_icon_links a' ).addClass( 'frm_button' );
4682 } else {
4683 $thisField.find( '.frm_repeat_text' ).hide();
4684 $thisField.find( '.repeat_icon_links a' ).removeClass( 'frm_button' );
4685 }
4686 }
4687
4688 function checkRepeatLimit() {
4689 /*jshint validthis:true */
4690 var val = this.value;
4691 if ( val !== '' && ( val < 2 || val > 200 ) ) {
4692 infoModal( frm_admin_js.repeat_limit_min );
4693 this.value = '';
4694 }
4695 }
4696
4697 function checkCheckboxSelectionsLimit() {
4698 /*jshint validthis:true */
4699 var val = this.value;
4700 if ( val !== '' && ( val < 1 || val > 200 ) ) {
4701 infoModal( frm_admin_js.checkbox_limit );
4702 this.value = '';
4703 }
4704 }
4705
4706 function updateRepeatText( obj, addRemove ) {
4707 var $thisField = jQuery( obj ).closest( '.frm_field_box' );
4708 $thisField.find( '.frm_' + addRemove + '_form_row .frm_repeat_label' ).text( obj.value );
4709 }
4710
4711 function fieldsInSection( id ) {
4712 var children = [];
4713 jQuery( document.getElementById( 'frm_field_id_' + id ) ).find( 'li.frm_field_box:not(.no_repeat_section .edit_field_type_end_divider)' ).each( function() {
4714 children.push( jQuery( this ).data( 'fid' ) );
4715 });
4716 return children;
4717 }
4718
4719 function toggleFormTax() {
4720 /*jshint validthis:true */
4721 var id = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
4722 var val = this.value;
4723 var $showFields = document.getElementById( 'frm_show_selected_fields_' + id );
4724 var $showForms = document.getElementById( 'frm_show_selected_forms_' + id );
4725
4726 jQuery( $showForms ).find( 'select' ).val( '' );
4727 if ( val === 'form' ) {
4728 $showForms.style.display = 'inline';
4729 empty( $showFields );
4730 } else {
4731 $showFields.style.display = 'none';
4732 $showForms.style.display = 'none';
4733 getTaxOrFieldSelection( val, id );
4734 }
4735
4736 }
4737
4738 function resetOptOnChange() {
4739 /*jshint validthis:true */
4740 var field, thisOpt;
4741
4742 field = getFieldKeyFromOpt( this );
4743 if ( ! field ) {
4744 return;
4745 }
4746
4747 thisOpt = jQuery( this ).closest( '.frm_single_option' );
4748
4749 resetSingleOpt( field.fieldId, field.fieldKey, thisOpt );
4750 }
4751
4752 function getFieldKeyFromOpt( object ) {
4753 var allOpts, fieldId, fieldKey;
4754
4755 allOpts = jQuery( object ).closest( '.frm_sortable_field_opts' );
4756 if ( ! allOpts.length ) {
4757 return false;
4758 }
4759
4760 fieldId = allOpts.attr( 'id' ).replace( 'frm_field_', '' ).replace( '_opts', '' );
4761 fieldKey = allOpts.data( 'key' );
4762
4763 return {
4764 fieldId: fieldId,
4765 fieldKey: fieldKey
4766 };
4767 }
4768
4769 function resetSingleOpt( fieldId, fieldKey, thisOpt ) {
4770 var saved, text, defaultVal, previewInput, labelForDisplay, optContainer,
4771 optKey = thisOpt.data( 'optkey' ),
4772 separateValues = usingSeparateValues( fieldId ),
4773 single = jQuery( 'label[for="field_' + fieldKey + '-' + optKey + '"]' ),
4774 baseName = 'field_options[options_' + fieldId + '][' + optKey + ']',
4775 label = jQuery( 'input[name="' + baseName + '[label]"]' );
4776
4777 if ( single.length < 1 ) {
4778 resetDisplayedOpts( fieldId );
4779
4780 // Set the default value.
4781 defaultVal = thisOpt.find( 'input[name^="default_value_"]' );
4782 if ( defaultVal.is( ':checked' ) && label.length > 0 ) {
4783 jQuery( 'select[name^="item_meta[' + fieldId + ']"]' ).val( label.val() );
4784 }
4785 return;
4786 }
4787
4788 previewInput = single.children( 'input' );
4789
4790 if ( label.length < 1 ) {
4791 // Check for other label.
4792 label = jQuery( 'input[name="' + baseName + '"]' );
4793 saved = label.val();
4794 } else if ( separateValues ) {
4795 saved = jQuery( 'input[name="' + baseName + '[value]"]' ).val();
4796 } else {
4797 saved = label.val();
4798 }
4799
4800 if ( label.length < 1 ) {
4801 return;
4802 }
4803
4804 // Set the displayed value.
4805 text = single[0].childNodes;
4806
4807 if ( imagesAsOptions( fieldId ) ) {
4808 labelForDisplay = getImageDisplayValue( thisOpt, fieldId, label );
4809 optContainer = single.find( '.frm_image_option_container' );
4810
4811 if ( optContainer.length > 0 ) {
4812 optContainer.replaceWith( labelForDisplay );
4813 } else {
4814 text[ text.length - 1 ].nodeValue = '';
4815 single.append( labelForDisplay );
4816 }
4817 } else {
4818 text[ text.length - 1 ].nodeValue = ' ' + label.val();
4819 }
4820
4821 // Set saved value.
4822 previewInput.val( saved );
4823
4824 // Set the default value.
4825 defaultVal = thisOpt.find( 'input[name^="default_value_"]' );
4826 previewInput.prop( 'checked', defaultVal.is( ':checked' ) ? true : false );
4827 }
4828
4829 /**
4830 * Set the displayed value for an image option.
4831 */
4832 function getImageDisplayValue( thisOpt, fieldId, label ) {
4833 var image, imageUrl, showLabelWithImage, fieldType;
4834
4835 image = thisOpt.find( 'img' );
4836 if ( image ) {
4837 imageUrl = image.attr( 'src' );
4838 }
4839
4840 showLabelWithImage = showingLabelWithImage( fieldId );
4841 fieldType = radioOrCheckbox( fieldId );
4842 return getImageLabel( label.val(), showLabelWithImage, imageUrl, fieldType );
4843 }
4844
4845 function getImageOptionSize( fieldId ) {
4846 var val,
4847 field = document.getElementById( 'field_options_image_size_' + fieldId ),
4848 size = '';
4849
4850 if ( field !== null ) {
4851 val = field.value;
4852 if ( val !== '' ) {
4853 size = val;
4854 }
4855 }
4856
4857 return size;
4858 }
4859
4860 function resetDisplayedOpts( fieldId ) {
4861 var i, opts, type, placeholder, fieldInfo,
4862 input = jQuery( '[name^="item_meta[' + fieldId + ']"]' );
4863
4864 if ( input.length < 1 ) {
4865 return;
4866 }
4867
4868 if ( input.is( 'select' ) ) {
4869 placeholder = document.getElementById( 'frm_placeholder_' + fieldId );
4870 if ( placeholder !== null && placeholder.value === '' ) {
4871 fillDropdownOpts( input[0], { sourceID: fieldId });
4872 } else {
4873 fillDropdownOpts( input[0], {
4874 sourceID: fieldId,
4875 placeholder: placeholder.value
4876 });
4877 }
4878 } else {
4879 opts = getMultipleOpts( fieldId );
4880 type = input.attr( 'type' );
4881 jQuery( '#field_' + fieldId + '_inner_container > .frm_form_fields' ).html( '' );
4882 fieldInfo = getFieldKeyFromOpt( jQuery( '#frm_delete_field_' + fieldId + '-000_container' ) );
4883
4884 var container = jQuery( '#field_' + fieldId + '_inner_container > .frm_form_fields' ),
4885 hasImageOptions = imagesAsOptions( fieldId ),
4886 imageSize = hasImageOptions ? getImageOptionSize( fieldId ) : '',
4887 imageOptionClass = hasImageOptions ? ( 'frm_image_option frm_image_' + imageSize + ' ' ) : '',
4888 isProduct = isProductField( fieldId );
4889
4890 for ( i = 0; i < opts.length; i++ ) {
4891 container.append( addRadioCheckboxOpt( type, opts[ i ], fieldId, fieldInfo.fieldKey, isProduct, imageOptionClass ) );
4892 }
4893 }
4894
4895 adjustConditionalLogicOptionOrders( fieldId );
4896 }
4897
4898 function adjustConditionalLogicOptionOrders( fieldId ) {
4899 var row, opts, logicId, valueSelect, rowOptions, expectedOrder, optionLength, optionIndex, expectedOption, optionMatch,
4900 rows = document.getElementById( 'frm_builder_page' ).querySelectorAll( '.frm_logic_row' ),
4901 rowLength = rows.length,
4902 fieldOptions = getFieldOptions( fieldId ),
4903 optionLength = fieldOptions.length;
4904
4905 for ( rowIndex = 0; rowIndex < rowLength; rowIndex++ ) {
4906 row = rows[ rowIndex ];
4907 opts = row.querySelector( '.frm_logic_field_opts' );
4908
4909 if ( opts.value != fieldId ) {
4910 continue;
4911 }
4912
4913 logicId = row.id.split( '_' )[ 2 ];
4914 valueSelect = row.querySelector( 'select[name="field_options[hide_opt_' + logicId + '][]"]' );
4915
4916 for ( optionIndex = optionLength - 1; optionIndex >= 0; optionIndex-- ) {
4917 expectedOption = fieldOptions[ optionIndex ];
4918 optionMatch = valueSelect.querySelector( 'option[value="' + expectedOption + '"]' );
4919
4920 if ( optionMatch === null ) {
4921 optionMatch = document.createElement( 'option' );
4922 optionMatch.setAttribute( 'value', expectedOption );
4923 optionMatch.textContent = expectedOption;
4924 }
4925
4926 valueSelect.prepend( optionMatch );
4927 }
4928
4929 optionMatch = valueSelect.querySelector( 'option[value=""]' );
4930 if ( optionMatch !== null ) {
4931 valueSelect.prepend( optionMatch );
4932 }
4933 }
4934 }
4935
4936 function getFieldOptions( fieldId ) {
4937 var index, input, li,
4938 listItems = document.getElementById( 'frm_field_' + fieldId + '_opts' ).querySelectorAll( '.frm_single_option' ),
4939 options = [],
4940 length = listItems.length;
4941 for ( index = 0; index < length; index++ ) {
4942 li = listItems[ index ];
4943
4944 if ( li.classList.contains( 'frm_hidden' ) ) {
4945 continue;
4946 }
4947
4948 input = li.querySelector( '.field_' + fieldId + '_option' );
4949 options.push( input.value );
4950 }
4951 return options;
4952 }
4953
4954 function addRadioCheckboxOpt( type, opt, fieldId, fieldKey, isProduct, classes ) {
4955 var other, single,
4956 isOther = opt.key.indexOf( 'other' ) !== -1,
4957
4958 id = 'field_' + fieldKey + '-' + opt.key;
4959
4960 other = '<input type="text" id="field_' + fieldKey + '-' + opt.key + '-otext" class="frm_other_input frm_pos_none" name="item_meta[other][' + fieldId + '][' + opt.key + ']" value="" />';
4961
4962 single = '<div class="frm_' + type + ' ' + type + ' ' + classes + '" id="frm_' + type + '_' + fieldId + '-' + opt.key + '"><label for="' + id +
4963 '"><input type="' + type +
4964 '" name="item_meta[' + fieldId + ']' + ( type === 'checkbox' ? '[]' : '' ) +
4965 '" value="' + opt.saved + '" id="' + id + '"' + ( isProduct ? ' data-price="' + opt.price + '"' : '' ) + ( opt.checked ? ' checked="checked"' : '' ) + '> ' + opt.label + '</label>' +
4966 ( isOther ? other : '' ) +
4967 '</div>';
4968
4969 return single;
4970 }
4971
4972 function fillDropdownOpts( field, atts ) {
4973 if ( field === null ) {
4974 return;
4975 }
4976 var sourceID = atts.sourceID,
4977 placeholder = atts.placeholder,
4978 isProduct = isProductField( sourceID ),
4979 showOther = atts.other;
4980
4981 removeDropdownOpts( field );
4982 var opts = getMultipleOpts( sourceID ),
4983 hasPlaceholder = ( typeof placeholder !== 'undefined' );
4984
4985 for ( var i = 0; i < opts.length; i++ ) {
4986 var label = opts[ i ].label,
4987 isOther = opts[ i ].key.indexOf( 'other' ) !== -1;
4988
4989 if ( hasPlaceholder && label !== '' ) {
4990 addBlankSelectOption( field, placeholder );
4991 } else if ( hasPlaceholder ) {
4992 label = placeholder;
4993 }
4994 hasPlaceholder = false;
4995
4996 if ( ! isOther || showOther ) {
4997 var opt = document.createElement( 'option' );
4998 opt.value = opts[ i ].saved;
4999 opt.innerHTML = label;
5000
5001 if ( isProduct ) {
5002 opt.setAttribute( 'data-price', opts[ i ].price );
5003 }
5004
5005 field.appendChild( opt );
5006 }
5007 }
5008 }
5009
5010 function addBlankSelectOption( field, placeholder ) {
5011 var opt = document.createElement( 'option' ),
5012 firstChild = field.firstChild;
5013
5014 opt.value = '';
5015 opt.innerHTML = placeholder;
5016 if ( firstChild !== null ) {
5017 field.insertBefore( opt, firstChild );
5018 field.selectedIndex = 0;
5019 } else {
5020 field.appendChild( opt );
5021 }
5022 }
5023
5024 function getMultipleOpts( fieldId ) {
5025 var i, saved, labelName, label, key, optObj,
5026 image, savedLabel, input, field, checkbox, fieldType,
5027 checked = false,
5028 opts = [],
5029 imageUrl = '',
5030
5031 optVals = jQuery( 'input[name^="field_options[options_' + fieldId + ']"]' ),
5032 separateValues = usingSeparateValues( fieldId ),
5033 hasImageOptions = imagesAsOptions( fieldId ),
5034 showLabelWithImage = showingLabelWithImage( fieldId ),
5035 isProduct = isProductField( fieldId );
5036
5037 for ( i = 0; i < optVals.length; i++ ) {
5038 if ( optVals[ i ].name.indexOf( '[000]' ) > 0 || optVals[ i ].name.indexOf( '[value]' ) > 0 || optVals[ i ].name.indexOf( '[image]' ) > 0 || optVals[ i ].name.indexOf( '[price]' ) > 0 ) {
5039 continue;
5040 }
5041 saved = optVals[ i ].value;
5042 label = saved;
5043 key = optVals[ i ].name.replace( 'field_options[options_' + fieldId + '][', '' ).replace( '[label]', '' ).replace( ']', '' );
5044
5045 if ( separateValues ) {
5046 labelName = optVals[ i ].name.replace( '[label]', '[value]' );
5047 saved = jQuery( 'input[name="' + labelName + '"]' ).val();
5048 }
5049
5050 if ( hasImageOptions ) {
5051 imageUrl = getImageUrlFromInput( optVals[i]);
5052 fieldType = radioOrCheckbox( fieldId );
5053 label = getImageLabel( label, showLabelWithImage, imageUrl, fieldType );
5054 }
5055
5056 /**
5057 * @since 5.0.04
5058 */
5059 label = frmAdminBuild.hooks.applyFilters( 'frm_choice_field_label', label, fieldId, optVals[ i ], hasImageOptions );
5060
5061 checked = getChecked( optVals[ i ].id );
5062
5063 optObj = {
5064 saved: saved,
5065 label: label,
5066 checked: checked,
5067 key: key
5068 };
5069
5070 if ( isProduct ) {
5071 labelName = optVals[ i ].name.replace( '[label]', '[price]' );
5072 optObj.price = jQuery( 'input[name="' + labelName + '"]' ).val();
5073 }
5074
5075 opts.push( optObj );
5076 }
5077
5078 return opts;
5079 }
5080
5081 function radioOrCheckbox( fieldId ) {
5082 var settings = document.getElementById( 'frm-single-settings-' + fieldId );
5083 if ( settings === null ) {
5084 return 'radio';
5085 }
5086
5087 return settings.classList.contains( 'frm-type-checkbox' ) ? 'checkbox' : 'radio';
5088 }
5089
5090 function getImageUrlFromInput( optVal ) {
5091 var img,
5092 wrapper = jQuery( optVal ).siblings( '.frm_image_preview_wrapper' );
5093
5094 if ( ! wrapper.length ) {
5095 return '';
5096 }
5097
5098 img = wrapper.find( 'img' );
5099 if ( ! img.length ) {
5100 return '';
5101 }
5102
5103 return img.attr( 'src' );
5104 }
5105
5106 function getImageLabel( label, showLabelWithImage, imageUrl, fieldType ) {
5107 var imageLabelClass, fullLabel,
5108 originalLabel = label,
5109 shape = fieldType === 'checkbox' ? 'square' : 'circle';
5110
5111 fullLabel = '<div class="frm_selected_checkmark"><svg class="frmsvg"><use xlink:href="#frm_checkmark_' + shape + '_icon"></svg></div>';
5112 if ( imageUrl ) {
5113 fullLabel += '<img src="' + imageUrl + '" alt="' + originalLabel + '" />';
5114 } else {
5115 fullLabel += '<div class="frm_empty_url">' + frm_admin_js.image_placeholder_icon + '</div>';
5116 }
5117 if ( showLabelWithImage ) {
5118 fullLabel += '<span class="frm_text_label_for_image"><span class="frm_text_label_for_image_inner">' + originalLabel + '</span></span>';
5119 }
5120
5121 imageLabelClass = showLabelWithImage ? ' frm_label_with_image' : '';
5122
5123 return ( '<span class="frm_image_option_container' + imageLabelClass + '">' + fullLabel + '</span>' );
5124 }
5125
5126 function getChecked( id ) {
5127 field = jQuery( '#' + id );
5128
5129 if ( field.length === 0 ) {
5130 return false;
5131 }
5132
5133 checkbox = field.siblings( 'input[type=checkbox]' );
5134
5135 return checkbox.length && checkbox.prop( 'checked' );
5136 }
5137
5138 function removeDropdownOpts( field ) {
5139 var i;
5140 if ( typeof field.options === 'undefined' ) {
5141 return;
5142 }
5143
5144 for ( i = field.options.length - 1; i >= 0; i-- ) {
5145 field.remove( i );
5146 }
5147 }
5148
5149 /**
5150 * Is the box checked to use separate values?
5151 */
5152 function usingSeparateValues( fieldId ) {
5153 return isChecked( 'separate_value_' + fieldId );
5154 }
5155
5156 /**
5157 * Is the box checked to use images as options?
5158 */
5159 function imagesAsOptions( fieldId ) {
5160 var checked = false,
5161 field = document.getElementsByName( 'field_options[image_options_' + fieldId + ']' );
5162
5163 for ( var i = 0; i < field.length; i++ ) {
5164 if ( field[ i ].checked ) {
5165 checked = '0' !== field[ i ].value;
5166 }
5167 }
5168
5169 /**
5170 * @since 5.0.04
5171 */
5172 return frmAdminBuild.hooks.applyFilters( 'frm_choice_field_images_as_options', checked, fieldId );
5173 }
5174
5175 function showingLabelWithImage( fieldId ) {
5176 const isShowing = ! isChecked( 'hide_image_text_' + fieldId );
5177
5178 /**
5179 * @since 5.0.04
5180 */
5181 return frmAdminBuild.hooks.applyFilters( 'frm_choice_field_showing_label_with_image', isShowing, fieldId );
5182 }
5183
5184 function isChecked( id ) {
5185 var field = document.getElementById( id );
5186 if ( field === null ) {
5187 return false;
5188 } else {
5189 return field.checked;
5190 }
5191 }
5192
5193 function checkUniqueOpt( targetInput ) {
5194 const settingsContainer = targetInput.closest( '.frm-single-settings' );
5195 const fieldId = settingsContainer.getAttribute( 'data-fid' );
5196 const areValuesSeparate = settingsContainer.querySelector( '[name="field_options[separate_value_' + fieldId + ']"]' ).checked;
5197
5198 if ( areValuesSeparate && ! targetInput.name.endsWith( '[value]' ) ) {
5199 return;
5200 }
5201
5202 const container = document.getElementById( 'frm_field_' + fieldId + '_opts' );
5203 const conflicts = Array.from( container.querySelectorAll( 'input[type="text"]' ) ).filter(
5204 input => input.id !== targetInput.id &&
5205 areValuesSeparate === input.name.endsWith( '[value]' ) &&
5206 input.value === targetInput.value
5207 );
5208 if ( conflicts.length ) {
5209 infoModal( __( 'Duplicate option value "%s" detected', 'formidable' ).replace( '%s', targetInput.value ) );
5210 }
5211 }
5212
5213 function setStarValues() {
5214 /*jshint validthis:true */
5215 var fieldID = this.id.replace( 'radio_maxnum_', '' );
5216 var container = jQuery( '#field_' + fieldID + '_inner_container .frm-star-group' );
5217 var fieldKey = document.getElementsByName( 'field_options[field_key_' + fieldID + ']' )[0].value;
5218 container.html( '' );
5219
5220 var min = 1;
5221 var max = this.value;
5222 if ( min > max ) {
5223 max = min;
5224 }
5225
5226 for ( var i = min; i <= max; i++ ) {
5227 container.append( '<input type="hidden" name="field_options[options_' + fieldID + '][' + i + ']" value="' + i + '"><input type="radio" name="item_meta[' + fieldID + ']" id="field_' + fieldKey + '-' + i + '" value="' + i + '" /><label for="field_' + fieldKey + '-' + i + '" class="star-rating"></label>' );
5228 }
5229 }
5230
5231 function setScaleValues() {
5232 /*jshint validthis:true */
5233 var isMin = this.id.indexOf( 'minnum' ) !== -1;
5234 var fieldID = this.id.replace( 'scale_maxnum_', '' ).replace( 'scale_minnum_', '' );
5235 var min = this.value;
5236 var max = this.value;
5237 if ( isMin ) {
5238 max = document.getElementById( 'scale_maxnum_' + fieldID ).value;
5239 } else {
5240 min = document.getElementById( 'scale_minnum_' + fieldID ).value;
5241 }
5242
5243 updateScaleValues( parseInt( min, 10 ), parseInt( max, 10 ), fieldID );
5244 }
5245
5246 function updateScaleValues( min, max, fieldID ) {
5247 var container = jQuery( '#field_' + fieldID + '_inner_container .frm_form_fields' );
5248 container.html( '' );
5249
5250 if ( min >= max ) {
5251 max = min + 1;
5252 }
5253
5254 for ( var i = min; i <= max; i++ ) {
5255 container.append( '<div class="frm_scale"><label><input type="hidden" name="field_options[options_' + fieldID + '][' + i + ']" value="' + i + '"> <input type="radio" name="item_meta[' + fieldID + ']" value="' + i + '"> ' + i + ' </label></div>' );
5256 }
5257 container.append( '<div class="clear"></div>' );
5258 }
5259
5260 function getFieldValues() {
5261 /*jshint validthis:true */
5262 var isTaxonomy,
5263 val = this.value;
5264
5265 if ( val ) {
5266 var parentIDs = this.parentNode.id.replace( 'frm_logic_', '' ).split( '_' );
5267 var fieldID = parentIDs[0];
5268 var metaKey = parentIDs[1];
5269 var valueField = document.getElementById( 'frm_field_id_' + val );
5270 var valueFieldType = valueField.getAttribute( 'data-ftype' );
5271 var fill = document.getElementById( 'frm_show_selected_values_' + fieldID + '_' + metaKey );
5272 var optionName = 'field_options[hide_opt_' + fieldID + '][]';
5273 var optionID = 'frm_field_logic_opt_' + fieldID;
5274 var input = false;
5275 var showSelect = ( valueFieldType === 'select' || valueFieldType === 'checkbox' || valueFieldType === 'radio' );
5276 var showText = ( valueFieldType === 'text' || valueFieldType === 'email' || valueFieldType === 'phone' || valueFieldType === 'url' || valueFieldType === 'number' );
5277
5278 if ( showSelect ) {
5279 isTaxonomy = document.getElementById( 'frm_has_hidden_options_' + val );
5280 if ( isTaxonomy !== null ) {
5281 // get the category options with ajax
5282 showSelect = false;
5283 }
5284 }
5285
5286 if ( showSelect || showText ) {
5287 fill.innerHTML = '';
5288 if ( showSelect ) {
5289 input = document.createElement( 'select' );
5290 } else {
5291 input = document.createElement( 'input' );
5292 input.type = 'text';
5293 }
5294 input.name = optionName;
5295 input.id = optionID + '_' + metaKey;
5296 fill.appendChild( input );
5297
5298 if ( showSelect ) {
5299 var fillField = document.getElementById( input.id );
5300 fillDropdownOpts( fillField, {
5301 sourceID: val,
5302 placeholder: '',
5303 other: true
5304 });
5305 }
5306 } else {
5307 var thisType = this.getAttribute( 'data-type' );
5308 frmGetFieldValues( val, fieldID, metaKey, thisType );
5309 }
5310 }
5311 }
5312
5313 function getFieldSelection() {
5314 /*jshint validthis:true */
5315 var formId = this.value;
5316 if ( formId ) {
5317 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
5318 getTaxOrFieldSelection( formId, fieldId );
5319 }
5320 }
5321
5322 function getTaxOrFieldSelection( formId, fieldId ) {
5323 if ( formId ) {
5324 jQuery.ajax({
5325 type: 'POST',
5326 url: ajaxurl,
5327 data: {
5328 action: 'frm_get_field_selection',
5329 field_id: fieldId,
5330 form_id: formId,
5331 nonce: frmGlobal.nonce
5332 },
5333 success: function( msg ) {
5334 jQuery( '#frm_show_selected_fields_' + fieldId ).html( msg ).show();
5335 }
5336 });
5337 }
5338 }
5339
5340 function updateFieldOrder() {
5341 var fields, fieldId, field, currentOrder, newOrder;
5342 renumberPageBreaks();
5343 jQuery( '#frm-show-fields' ).each( function( i ) {
5344 fields = jQuery( 'li.frm_field_box', this );
5345 for ( i = 0; i < fields.length; i++ ) {
5346 fieldId = fields[ i ].getAttribute( 'data-fid' );
5347 field = jQuery( 'input[name="field_options[field_order_' + fieldId + ']"]' );
5348 currentOrder = field.val();
5349 newOrder = i + 1;
5350
5351 if ( currentOrder != newOrder ) {
5352 field.val( newOrder );
5353 singleField = document.getElementById( 'frm-single-settings-' + fieldId );
5354
5355 moveFieldSettings( singleField );
5356 fieldUpdated();
5357 }
5358 }
5359 });
5360 }
5361
5362 function toggleSectionHolder() {
5363 document.querySelectorAll( '.start_divider' ).forEach(
5364 function( divider ) {
5365 toggleOneSectionHolder( jQuery( divider ) );
5366 }
5367 );
5368 }
5369
5370 function toggleOneSectionHolder( $section ) {
5371 var noSectionFields, $rows, length, index, sectionHasFields;
5372
5373 if ( ! $section.length ) {
5374 return;
5375 }
5376
5377 $rows = $section.find( 'ul.frm_sorting' );
5378 sectionHasFields = false;
5379 length = $rows.length;
5380 for ( index = 0; index < length; ++index ) {
5381 if ( 0 !== getFieldsInRow( jQuery( $rows.get( index ) ) ).length ) {
5382 sectionHasFields = true;
5383 break;
5384 }
5385 }
5386
5387 noSectionFields = $section.parent().children( '.frm_no_section_fields' ).get( 0 );
5388 noSectionFields.classList.toggle( 'frm_block', ! sectionHasFields );
5389 }
5390
5391 function slideDown() {
5392 /*jshint validthis:true */
5393 var id = jQuery( this ).data( 'slidedown' );
5394 var $thisId = jQuery( document.getElementById( id ) );
5395 if ( $thisId.is( ':hidden' ) ) {
5396 $thisId.slideDown( 'fast' );
5397 this.style.display = 'none';
5398 }
5399 return false;
5400 }
5401
5402 function slideUp() {
5403 /*jshint validthis:true */
5404 var id = jQuery( this ).data( 'slideup' );
5405 var $thisId = jQuery( document.getElementById( id ) );
5406 $thisId.slideUp( 'fast' );
5407 $thisId.siblings( 'a' ).show();
5408 return false;
5409 }
5410
5411 function adjustVisibilityValuesForEveryoneValues( element, option ) {
5412 if ( '' === option.getAttribute( 'value' ) ) {
5413 onEveryoneOptionSelected( jQuery( this ) );
5414 } else {
5415 unselectEveryoneOptionIfSelected( jQuery( this ) );
5416 }
5417 }
5418
5419 function onEveryoneOptionSelected( $select ) {
5420 $select.val( '' );
5421 $select.next( '.btn-group' ).find( '.multiselect-container input[value!=""]' ).prop( 'checked', false );
5422 }
5423
5424 function unselectEveryoneOptionIfSelected( $select ) {
5425 var selectedValues = $select.val(),
5426 index;
5427
5428 if ( selectedValues === null ) {
5429 $select.next( '.btn-group' ).find( '.multiselect-container input[value=""]' ).prop( 'checked', true );
5430 onEveryoneOptionSelected( $select );
5431 return;
5432 }
5433
5434 index = selectedValues.indexOf( '' );
5435 if ( index >= 0 ) {
5436 selectedValues.splice( index, 1 );
5437 $select.val( selectedValues );
5438 $select.next( '.btn-group' ).find( '.multiselect-container input[value=""]' ).prop( 'checked', false );
5439 }
5440 }
5441
5442 /**
5443 * Get rid of empty container that inserts extra space.
5444 */
5445 function hideEmptyEle() {
5446 jQuery( '.frm-hide-empty' ).each( function() {
5447 if ( jQuery( this ).text().trim().length === 0 ) {
5448 jQuery( this ).remove();
5449 }
5450 });
5451 }
5452
5453 /* Change the classes in the builder */
5454 function changeFieldClass( field, setting ) {
5455 var classes, replace, alignField,
5456 replaceWith = ' ' + setting.value,
5457 fieldId = field.getAttribute( 'data-fid' );
5458
5459 // Include classes from multiple settings.
5460 if ( typeof fieldId !== 'undefined' ) {
5461 if ( setting.classList.contains( 'field_options_align' ) ) {
5462 replaceWith += ' ' + document.getElementById( 'frm_classes_' + fieldId ).value;
5463 } else if ( setting.classList.contains( 'frm_classes' ) ) {
5464 alignField = document.getElementById( 'field_options_align_' + fieldId );
5465 if ( alignField !== null ) {
5466 replaceWith += ' ' + alignField.value;
5467 }
5468 }
5469 }
5470 replaceWith += ' ';
5471
5472 // Allow for the column number dropdown.
5473 replaceWith = replaceWith.replace( ' block ', ' ' ).replace( ' inline ', ' horizontal_radio ' );
5474
5475 classes = field.className.split( ' frmstart ' )[1];
5476 classes = 0 === classes.indexOf( 'frmend ' ) ? '' : classes.split( ' frmend ' )[0];
5477
5478 if ( classes.trim() === '' ) {
5479 replace = ' frmstart frmend ';
5480 if ( -1 === field.className.indexOf( replace ) ) {
5481 replace = ' frmstart frmend ';
5482 }
5483 replaceWith = ' frmstart ' + replaceWith.trim() + ' frmend ';
5484 } else {
5485 replace = classes.trim();
5486 replaceWith = replaceWith.trim();
5487 }
5488
5489 field.className = field.className.replace( replace, replaceWith );
5490 }
5491
5492 function maybeShowInlineModal( e ) {
5493 /*jshint validthis:true */
5494 e.preventDefault();
5495 showInlineModal( this );
5496 }
5497
5498 function showInlineModal( icon, input ) {
5499 var box = document.getElementById( icon.getAttribute( 'data-open' ) ),
5500 container = jQuery( icon ).closest( 'p' ),
5501 inputTrigger = ( typeof input !== 'undefined' );
5502
5503 if ( container.hasClass( 'frm-open' ) ) {
5504 container.removeClass( 'frm-open' );
5505 box.classList.add( 'frm_hidden' );
5506 } else {
5507 if ( ! inputTrigger ) {
5508 input = getInputForIcon( icon );
5509 }
5510 if ( input !== null ) {
5511 if ( ! inputTrigger ) {
5512 input.focus();
5513 }
5514 container.after( box );
5515 box.setAttribute( 'data-fills', input.id );
5516
5517 if ( box.id.indexOf( 'frm-calc-box' ) === 0 ) {
5518 popCalcFields( box, true );
5519 }
5520 }
5521
5522 container.addClass( 'frm-open' );
5523 box.classList.remove( 'frm_hidden' );
5524 }
5525 }
5526
5527 function dismissInlineModal( e ) {
5528 /*jshint validthis:true */
5529 e.preventDefault();
5530 this.parentNode.classList.add( 'frm_hidden' );
5531 jQuery( '.frm-open [data-open="' + this.parentNode.id + '"]' ).closest( '.frm-open' ).removeClass( 'frm-open' );
5532 }
5533
5534 function changeInputtedValue() {
5535 /*jshint validthis:true */
5536 var i,
5537 action = this.getAttribute( 'data-frmchange' ).split( ',' );
5538
5539 for ( i = 0; i < action.length; i++ ) {
5540 if ( action[i] === 'updateOption' ) {
5541 changeHiddenSeparateValue( this );
5542 } else if ( action[i] === 'updateDefault' ) {
5543 changeDefaultRadioValue( this );
5544 } else if ( action[i] === 'checkUniqueOpt' ) {
5545 checkUniqueOpt( this );
5546 } else {
5547 this.value = this.value[ action[i] ]();
5548 }
5549 }
5550 }
5551
5552 /**
5553 * When the saved value is changed, update the default value radio.
5554 */
5555 function changeDefaultRadioValue( input ) {
5556 var parentLi = getOptionParent( input ),
5557 key = parentLi.getAttribute( 'data-optkey' ),
5558 fieldId = getOptionFieldId( parentLi, key ),
5559 defaultRadio = parentLi.querySelector( 'input[name="default_value_' + fieldId + '"]' );
5560
5561 if ( defaultRadio !== null ) {
5562 defaultRadio.value = input.value;
5563 }
5564 }
5565
5566 /**
5567 * If separate values are not enabled, change the saved value when
5568 * the displayed value is changed.
5569 */
5570 function changeHiddenSeparateValue( input ) {
5571 var savedVal,
5572 parentLi = getOptionParent( input ),
5573 key = parentLi.getAttribute( 'data-optkey' ),
5574 fieldId = getOptionFieldId( parentLi, key ),
5575 sep = document.getElementById( 'separate_value_' + fieldId );
5576
5577 if ( sep !== null && sep.checked === false ) {
5578 // If separate values are not turned on.
5579 savedVal = document.getElementById( 'field_key_' + fieldId + '-' + key );
5580 savedVal.value = input.value;
5581 changeDefaultRadioValue( savedVal );
5582 }
5583 }
5584
5585 function getOptionParent( input ) {
5586 var parentLi = input.parentNode;
5587 if ( parentLi.tagName !== 'LI' ) {
5588 parentLi = parentLi.parentNode;
5589 }
5590 return parentLi;
5591 }
5592
5593 function getOptionFieldId( li, key ) {
5594 var liId = li.id;
5595
5596 return liId.replace( 'frm_delete_field_', '' ).replace( '-' + key + '_container', '' );
5597 }
5598
5599 function submitBuild() {
5600 /*jshint validthis:true */
5601 var $thisEle = jQuery( this );
5602 var p = $thisEle.html();
5603
5604 preFormSave( this );
5605
5606 var $form = jQuery( builderForm );
5607 var v = JSON.stringify( $form.serializeArray() );
5608
5609 jQuery( document.getElementById( 'frm_compact_fields' ) ).val( v );
5610 jQuery.ajax({
5611 type: 'POST',
5612 url: ajaxurl,
5613 data: {action: 'frm_save_form', 'frm_compact_fields': v, nonce: frmGlobal.nonce},
5614 success: function( msg ) {
5615 afterFormSave( $thisEle, p );
5616
5617 var $postStuff = document.getElementById( 'post-body-content' );
5618 var $html = document.createElement( 'div' );
5619 $html.setAttribute( 'class', 'frm_updated_message' );
5620 $html.innerHTML = msg;
5621 $postStuff.insertBefore( $html, $postStuff.firstChild );
5622 },
5623 error: function() {
5624 triggerSubmit( document.getElementById( 'frm_js_build_form' ) );
5625 }
5626 });
5627 }
5628
5629 function triggerSubmit( form ) {
5630 var button = form.ownerDocument.createElement( 'input' );
5631 button.style.display = 'none';
5632 button.type = 'submit';
5633 form.appendChild( button ).click();
5634 form.removeChild( button );
5635 }
5636
5637 function triggerChange( element ) {
5638 jQuery( element ).trigger( 'change' );
5639 }
5640
5641 function submitNoAjax() {
5642 /*jshint validthis:true */
5643 var form;
5644
5645 preFormSave( this );
5646 form = jQuery( builderForm );
5647 jQuery( document.getElementById( 'frm_compact_fields' ) ).val( JSON.stringify( form.serializeArray() ) );
5648 triggerSubmit( document.getElementById( 'frm_js_build_form' ) );
5649 }
5650
5651 function preFormSave( b ) {
5652 removeWPUnload();
5653 if ( jQuery( 'form.inplace_form' ).length ) {
5654 jQuery( '.inplace_save, .postbox' ).trigger( 'click' );
5655 }
5656
5657 $button = jQuery( b );
5658
5659 if ( $button.hasClass( 'frm_button_submit' ) ) {
5660 $button.addClass( 'frm_loading_form' );
5661 $button.html( frm_admin_js.saving );
5662 } else {
5663 $button.addClass( 'frm_loading_button' );
5664 $button.val( frm_admin_js.saving );
5665 }
5666 }
5667
5668 function afterFormSave( $button, buttonVal ) {
5669 $button.removeClass( 'frm_loading_form' ).removeClass( 'frm_loading_button' );
5670 $button.html( frm_admin_js.saved );
5671 resetOptionTextDetails();
5672 fieldsUpdated = 0;
5673
5674 setTimeout( function() {
5675 jQuery( '.frm_updated_message' ).fadeOut( 'slow', function() {
5676 this.parentNode.removeChild( this );
5677 });
5678 $button.fadeOut( 'slow', function() {
5679 $button.html( buttonVal );
5680 $button.show();
5681 });
5682 }, 5000 );
5683 }
5684
5685 function initUpgradeModal() {
5686 const $info = initModal( '#frm_upgrade_modal' );
5687 if ( $info === false ) {
5688 return;
5689 }
5690
5691 document.addEventListener( 'click', handleUpgradeClick );
5692
5693 function handleUpgradeClick( event ) {
5694 let element, upgradeLabel, link, content;
5695
5696 element = event.target;
5697 upgradeLabel = element.dataset.upgrade;
5698
5699 if ( ! upgradeLabel ) {
5700 const parent = element.closest( '[data-upgrade]' );
5701 if ( ! parent ) {
5702 return;
5703 }
5704
5705 element = parent;
5706 upgradeLabel = parent.dataset.upgrade;
5707 }
5708
5709 if ( element.classList.contains( 'frm_show_expired_modal' ) ) {
5710 const hookName = 'frm_show_expired_modal';
5711 wp.hooks.doAction( hookName, element );
5712 return;
5713 }
5714
5715 if ( ! upgradeLabel || element.classList.contains( 'frm_show_upgrade_tab' ) ) {
5716 return;
5717 }
5718
5719 event.preventDefault();
5720
5721 const modal = $info.get( 0 );
5722 const lockIcon = modal.querySelector( '.frm_lock_icon' );
5723
5724 if ( lockIcon ) {
5725 lockIcon.style.display = 'block';
5726 lockIcon.classList.remove( 'frm_lock_open_icon' );
5727 lockIcon.querySelector( 'use' ).setAttribute( 'href', '#frm_lock_icon' );
5728 }
5729
5730 const upgradeImageId = 'frm_upgrade_modal_image';
5731 const oldImage = document.getElementById( upgradeImageId );
5732 if ( oldImage ) {
5733 oldImage.remove();
5734 }
5735
5736 if ( element.dataset.image ) {
5737 if ( lockIcon ) {
5738 lockIcon.style.display = 'none';
5739 }
5740 lockIcon.parentNode.insertBefore( img({ id: upgradeImageId, src: frmGlobal.url + '/images/' + element.dataset.image }), lockIcon );
5741 }
5742
5743 const level = modal.querySelector( '.license-level' );
5744 if ( level ) {
5745 level.textContent = getRequiredLicenseFromTrigger( element );
5746 }
5747
5748 // If one click upgrade, hide other content
5749 addOneClick( element, 'modal', upgradeLabel );
5750
5751 modal.querySelector( '.frm_are_not_installed' ).style.display = element.dataset.image ? 'none' : 'inline-block';
5752 modal.querySelector( '.frm_feature_label' ).textContent = upgradeLabel;
5753 modal.querySelector( 'h2' ).style.display = 'block';
5754
5755 $info.dialog( 'open' );
5756
5757 // set the utm medium
5758 const button = modal.querySelector( '.button-primary:not(.frm-oneclick-button)' );
5759 link = button.getAttribute( 'href' ).replace( /(medium=)[a-z_-]+/ig, '$1' + element.getAttribute( 'data-medium' ) );
5760 content = element.getAttribute( 'data-content' );
5761 if ( content === null ) {
5762 content = '';
5763 }
5764 link = link.replace( /(content=)[a-z_-]+/ig, '$1' + content );
5765 button.setAttribute( 'href', link );
5766 }
5767 }
5768
5769 // Move the top banner above the screen options to prevent overlap.
5770 function moveTopBanner() {
5771 const $banner = document.querySelector( '.frm-banner-alert' ) || document.querySelector( '.frm-upgrade-bar' );
5772 if ( ! $banner ) {
5773 return;
5774 }
5775
5776 const $screenMeta = document.getElementById( 'screen-meta' );
5777 if ( ! $screenMeta ) {
5778 return;
5779 }
5780
5781 const $parentDiv = document.getElementById( 'wpbody-content' );
5782 $parentDiv.insertBefore( $banner, $screenMeta );
5783 }
5784
5785 function getRequiredLicenseFromTrigger( element ) {
5786 if ( element.dataset.requires ) {
5787 return element.dataset.requires;
5788 }
5789 return 'Pro';
5790 }
5791
5792 function populateUpgradeTab( element ) {
5793 const title = element.dataset.upgrade;
5794
5795 const tab = element.getAttribute( 'href' ).replace( '#', '' );
5796 const container = document.querySelector( '.frm_' + tab ) || document.querySelector( '.' + tab );
5797
5798 if ( ! container ) {
5799 return;
5800 }
5801
5802 if ( container.querySelector( '.frm-upgrade-message' ) ) {
5803 // Tab has already been populated.
5804 return;
5805 }
5806
5807 const h2 = container.querySelector( 'h2' );
5808 h2.style.borderBottom = 'none';
5809
5810 /* translators: %s: Form Setting section name (ie Form Permissions, Form Scheduling). */
5811 h2.textContent = __( '%s are not installed' ).replace( '%s', title );
5812
5813 container.classList.add( 'frmcenter' );
5814
5815 const upgradeModal = document.getElementById( 'frm_upgrade_modal' );
5816 appendClonedModalElementToContainer( 'frm-oneclick' );
5817 appendClonedModalElementToContainer( 'frm-addon-status' );
5818
5819 // Borrow the call to action from the Upgrade upgradeModal which should exist on the settings page (it is still used for other upgrades including Actions).
5820 const upgradeModalLink = upgradeModal.querySelector( '.frm-upgrade-link' );
5821 if ( upgradeModalLink ) {
5822 const upgradeButton = upgradeModalLink.cloneNode( true );
5823 const level = upgradeButton.querySelector( '.license-level' );
5824
5825 if ( level ) {
5826 level.textContent = getRequiredLicenseFromTrigger( element );
5827 }
5828
5829 container.appendChild( upgradeButton );
5830
5831 // Maybe append the secondary "Already purchased?" link from the upgradeModal as well.
5832 if ( upgradeModalLink.nextElementSibling && upgradeModalLink.nextElementSibling.querySelector( '.frm-link-secondary' ) ) {
5833 container.appendChild( upgradeModalLink.nextElementSibling.cloneNode( true ) );
5834 }
5835
5836 appendClonedModalElementToContainer( 'frm-oneclick-button' );
5837 }
5838
5839 appendClonedModalElementToContainer( 'frm-upgrade-message' );
5840 addOneClick( element, 'tab', element.dataset.message );
5841
5842 if ( element.dataset.screenshot ) {
5843 container.appendChild( getScreenshotWrapper( element.dataset.screenshot ) );
5844 }
5845
5846 function appendClonedModalElementToContainer( className ) {
5847 container.appendChild( upgradeModal.querySelector( '.' + className ).cloneNode( true ) );
5848 }
5849 }
5850
5851 function getScreenshotWrapper( screenshot ) {
5852 const folderUrl = frmGlobal.url + '/images/screenshots/';
5853 const wrapper = div({
5854 className: 'frm-settings-screenshot-wrapper',
5855 children: [
5856 getToolbar(),
5857 div({ child: img({ src: folderUrl + screenshot }) })
5858 ]
5859 });
5860
5861 function getToolbar() {
5862 const children = getColorIcons();
5863 children.push( img({ src: frmGlobal.url + '/images/tab.svg' }) );
5864 return div({
5865 className: 'frm-settings-screenshot-toolbar',
5866 children
5867 });
5868 }
5869
5870 function getColorIcons() {
5871 return [ '#ED8181', '#EDE06A', '#80BE30' ].map(
5872 color => {
5873 const circle = div({ className: 'frm-minmax-icon' });
5874 circle.style.backgroundColor = color;
5875 return circle;
5876 }
5877 );
5878 }
5879
5880 return wrapper;
5881 }
5882
5883 /**
5884 * Allow addons to be installed from the upgrade modal.
5885 *
5886 * @param {Element} link
5887 * @param {String} context Either 'modal' or 'tab'.
5888 * @param {String|undefined} upgradeLabel
5889 */
5890 function addOneClick( link, context, upgradeLabel ) {
5891 let container;
5892
5893 if ( 'modal' === context ) {
5894 container = document.getElementById( 'frm_upgrade_modal' );
5895 } else if ( 'tab' === context ) {
5896 container = document.getElementById( link.getAttribute( 'href' ).substr( 1 ) );
5897 } else {
5898 return;
5899 }
5900
5901 const oneclickMessage = container.querySelector( '.frm-oneclick' );
5902 const upgradeMessage = container.querySelector( '.frm-upgrade-message' );
5903 const showLink = container.querySelector( '.frm-upgrade-link' );
5904 const button = container.querySelector( '.frm-oneclick-button' );
5905 const addonStatus = container.querySelector( '.frm-addon-status' );
5906
5907 let oneclick = link.getAttribute( 'data-oneclick' );
5908 let newMessage = link.getAttribute( 'data-message' );
5909 let showIt = 'block';
5910 let showMsg = 'block';
5911 let hideIt = 'none';
5912
5913 // If one click upgrade, hide other content.
5914 if ( oneclickMessage !== null && typeof oneclick !== 'undefined' && oneclick ) {
5915 if ( newMessage === null ) {
5916 showMsg = 'none';
5917 }
5918 showIt = 'none';
5919 hideIt = 'block';
5920 oneclick = JSON.parse( oneclick );
5921
5922 button.className = button.className.replace( ' frm-install-addon', '' ).replace( ' frm-activate-addon', '' );
5923 button.className = button.className + ' ' + oneclick.class;
5924 button.textContent = __( 'Activate', 'formidable' );
5925 button.rel = oneclick.url;
5926 }
5927
5928 if ( ! newMessage ) {
5929 newMessage = upgradeMessage.getAttribute( 'data-default' );
5930 }
5931 if ( undefined !== upgradeLabel ) {
5932 newMessage = newMessage.replace( '<span class="frm_feature_label"></span>', upgradeLabel );
5933 }
5934
5935 upgradeMessage.innerHTML = newMessage;
5936
5937 // Either set the link or use the default.
5938 showLink.href = getShowLinkHrefValue( link, showLink );
5939
5940 addonStatus.style.display = 'none';
5941
5942 oneclickMessage.style.display = hideIt;
5943 button.style.display = hideIt === 'block' ? 'inline-block' : hideIt;
5944 upgradeMessage.style.display = showMsg;
5945 showLink.style.display = showIt === 'block' ? 'inline-block' : showIt;
5946 }
5947
5948 function getShowLinkHrefValue( link, showLink ) {
5949 let customLink = link.getAttribute( 'data-link' );
5950 if ( customLink === null || typeof customLink === 'undefined' || customLink === '' ) {
5951 customLink = showLink.getAttribute( 'data-default' );
5952 }
5953 return customLink;
5954 }
5955
5956 /* Form settings */
5957
5958 function showInputIcon( parentClass ) {
5959 if ( typeof parentClass === 'undefined' ) {
5960 parentClass = '';
5961 }
5962 maybeAddFieldSelection( parentClass );
5963 jQuery( parentClass + ' .frm_has_shortcodes:not(.frm-with-right-icon) input,' + parentClass + ' .frm_has_shortcodes:not(.frm-with-right-icon) textarea' ).wrap( '<span class="frm-with-right-icon"></span>' ).before( '<svg class="frmsvg frm-show-box"><use xlink:href="#frm_more_horiz_solid_icon"/></svg>' );
5964 }
5965
5966 /**
5967 * For reverse compatibility. Check for fields that were
5968 * using the old sidebar.
5969 */
5970 function maybeAddFieldSelection( parentClass ) {
5971 var i,
5972 missingClass = jQuery( parentClass + ' :not(.frm_has_shortcodes) .frm_not_email_message, ' + parentClass + ' :not(.frm_has_shortcodes) .frm_not_email_to, ' + parentClass + ' :not(.frm_has_shortcodes) .frm_not_email_subject' );
5973 for ( i = 0; i < missingClass.length; i++ ) {
5974 missingClass[i].parentNode.classList.add( 'frm_has_shortcodes' );
5975 }
5976 }
5977
5978 function showSuccessOpt() {
5979 /*jshint validthis:true */
5980 var c = 'success';
5981 if ( this.name === 'options[edit_action]' ) {
5982 c = 'edit';
5983 }
5984 var v = jQuery( this ).val();
5985 jQuery( '.' + c + '_action_box' ).hide();
5986 if ( v === 'redirect' ) {
5987 jQuery( '.' + c + '_action_redirect_box.' + c + '_action_box' ).fadeIn( 'slow' );
5988 } else if ( v === 'page' ) {
5989 jQuery( '.' + c + '_action_page_box.' + c + '_action_box' ).fadeIn( 'slow' );
5990 } else {
5991 jQuery( '.' + c + '_action_message_box.' + c + '_action_box' ).fadeIn( 'slow' );
5992 }
5993 }
5994
5995 function copyFormAction( event ) {
5996 if ( waitForActionToLoadBeforeCopy( event.target ) ) {
5997 return;
5998 }
5999
6000 const targetSettings = event.target.closest( '.frm_form_action_settings' );
6001 const wysiwyg = targetSettings.querySelector( '.wp-editor-area' );
6002 if ( wysiwyg ) {
6003 // Temporary remove TinyMCE before cloning to avoid TinyMCE conflicts.
6004 tinymce.EditorManager.execCommand( 'mceRemoveEditor', true, wysiwyg.id );
6005 }
6006
6007 const $action = jQuery( targetSettings ).clone();
6008 const currentID = $action.attr( 'id' ).replace( 'frm_form_action_', '' );
6009 const newID = newActionId( currentID );
6010
6011 $action.find( '.frm_action_id, .frm-btn-group' ).remove();
6012 $action.find( 'input[name$="[' + currentID + '][ID]"]' ).val( '' );
6013 $action.find( '.widget-inside' ).hide();
6014
6015 // the .html() gets original values, so they need to be set
6016 $action.find( 'input[type=text], textarea, input[type=number]' ).prop( 'defaultValue', function() {
6017 return this.value;
6018 });
6019
6020 $action.find( 'input[type=checkbox], input[type=radio]' ).prop( 'defaultChecked', function() {
6021 return this.checked;
6022 });
6023
6024 const rename = new RegExp( '\\[' + currentID + '\\]', 'g' );
6025 const reid = new RegExp( '_' + currentID + '"', 'g' );
6026 const reclass = new RegExp( '-' + currentID + '"', 'g' );
6027 const revalue = new RegExp( '"' + currentID + '"', 'g' ); // if a field id matches, this could cause trouble
6028
6029 let html = $action.html().replace( rename, '[' + newID + ']' ).replace( reid, '_' + newID + '"' );
6030 html = html.replace( reclass, '-' + newID + '"' ).replace( revalue, '"' + newID + '"' );
6031
6032 const newAction = div({
6033 id: 'frm_form_action_' + newID,
6034 className: $action.get( 0 ).className
6035 });
6036 newAction.setAttribute( 'data-actionkey', newID );
6037 newAction.innerHTML = html;
6038 newAction.querySelectorAll( '.wp-editor-wrap, .wp-editor-wrap *' ).forEach(
6039 element => {
6040 if ( 'string' === typeof element.className ) {
6041 element.className = element.className.replace( currentID, newID );
6042 }
6043 element.id = element.id.replace( currentID, newID );
6044 }
6045 );
6046 newAction.classList.remove( 'open' );
6047 document.getElementById( 'frm_notification_settings' ).appendChild( newAction );
6048
6049 if ( wysiwyg ) {
6050 // Re-initialize the original wysiwyg which was removed before cloning.
6051 frmDom.wysiwyg.init( wysiwyg );
6052 }
6053 frmDom.wysiwyg.init( newAction.querySelector( '.wp-editor-area' ) );
6054
6055 initiateMultiselect();
6056
6057 const hookName = 'frm_after_duplicate_action';
6058 wp.hooks.doAction( hookName, newAction );
6059 }
6060
6061 function waitForActionToLoadBeforeCopy( element ) {
6062 var $trigger = jQuery( element ),
6063 $original = $trigger.closest( '.frm_form_action_settings' ),
6064 $inside = $original.find( '.widget-inside' ),
6065 $top;
6066
6067 if ( $inside.find( 'p, div, table' ).length ) {
6068 return false;
6069 }
6070
6071 $top = $original.find( '.widget-top' );
6072 $top.on( 'frm-action-loaded', function() {
6073 $trigger.trigger( 'click' );
6074 $original.removeClass( 'open' );
6075 $inside.hide();
6076 });
6077 $top.trigger( 'click' );
6078 return true;
6079 }
6080
6081 function newActionId( currentID ) {
6082 var newID = parseInt( currentID, 10 ) + 11;
6083 var exists = document.getElementById( 'frm_form_action_' + newID );
6084 if ( exists !== null ) {
6085 newID++;
6086 newID = newActionId( newID );
6087 }
6088 return newID;
6089 }
6090
6091 function addFormAction() {
6092 /*jshint validthis:true */
6093 const type = jQuery( this ).data( 'actiontype' );
6094
6095 if ( isAtLimitForActionType( type ) ) {
6096 return;
6097 }
6098
6099 const actionId = getNewActionId();
6100 const formId = thisFormId;
6101
6102 const placeholderSetting = document.createElement( 'div' );
6103 placeholderSetting.classList.add( 'frm_single_' + type + '_settings' );
6104
6105 const actionsList = document.getElementById( 'frm_notification_settings' );
6106 actionsList.appendChild( placeholderSetting );
6107
6108 jQuery.ajax({
6109 type: 'POST',
6110 url: ajaxurl,
6111 data: {
6112 action: 'frm_add_form_action',
6113 type: type,
6114 list_id: actionId,
6115 form_id: formId,
6116 nonce: frmGlobal.nonce
6117 },
6118 success: handleAddFormActionSuccess
6119 });
6120
6121 function handleAddFormActionSuccess( html ) {
6122 fieldUpdated();
6123 placeholderSetting.remove();
6124
6125 closeOpenActions();
6126
6127 const newActionContainer = div();
6128 newActionContainer.innerHTML = html;
6129
6130 const widgetTop = newActionContainer.querySelector( '.widget-top' );
6131 Array.from( newActionContainer.children ).forEach( child => actionsList.appendChild( child ) );
6132
6133 jQuery( '.frm_form_action_settings' ).fadeIn( 'slow' );
6134
6135 const newAction = document.getElementById( 'frm_form_action_' + actionId );
6136
6137 newAction.classList.add( 'open' );
6138 document.getElementById( 'post-body-content' ).scroll({
6139 top: newAction.offsetTop + 10,
6140 left: 0,
6141 behavior: 'smooth'
6142 });
6143
6144 // Check if icon should be active
6145 checkActiveAction( type );
6146 showInputIcon( '#frm_form_action_' + actionId );
6147
6148 initiateMultiselect();
6149
6150 if ( widgetTop ) {
6151 jQuery( widgetTop ).trigger( 'frm-action-loaded' );
6152 }
6153 }
6154 }
6155
6156 function closeOpenActions() {
6157 document.querySelectorAll( '.frm_form_action_settings.open' ).forEach(
6158 setting => setting.classList.remove( 'open' )
6159 );
6160 }
6161
6162 function toggleActionGroups() {
6163 /*jshint validthis:true */
6164 var actions = document.getElementById( 'frm_email_addon_menu' ).classList,
6165 search = document.getElementById( 'actions-search-input' );
6166
6167 if ( actions.contains( 'frm-all-actions' ) ) {
6168 actions.remove( 'frm-all-actions' );
6169 actions.add( 'frm-limited-actions' );
6170 } else {
6171 actions.add( 'frm-all-actions' );
6172 actions.remove( 'frm-limited-actions' );
6173 }
6174
6175 // Reset search.
6176 search.value = '';
6177 triggerEvent( search, 'input' );
6178 }
6179
6180 function getNewActionId() {
6181 var actionSettings = document.querySelectorAll( '.frm_form_action_settings' ),
6182 len = getNewRowId( actionSettings, 'frm_form_action_' );
6183 if ( typeof document.getElementById( 'frm_form_action_' + len ) !== 'undefined' ) {
6184 len = len + 100;
6185 }
6186 if ( lastNewActionIdReturned >= len ) {
6187 len = lastNewActionIdReturned + 1;
6188 }
6189 lastNewActionIdReturned = len;
6190 return len;
6191 }
6192
6193 function clickAction( obj ) {
6194 var $thisobj = jQuery( obj );
6195
6196 if ( obj.className.indexOf( 'selected' ) !== -1 ) {
6197 return;
6198 }
6199 if ( obj.className.indexOf( 'edit_field_type_end_divider' ) !== -1 && $thisobj.closest( '.edit_field_type_divider' ).hasClass( 'no_repeat_section' ) ) {
6200 return;
6201 }
6202
6203 deselectFields();
6204 $thisobj.addClass( 'selected' );
6205 showFieldOptions( obj );
6206 }
6207
6208 /**
6209 * When a field is selected, show the field settings in the sidebar.
6210 */
6211 function showFieldOptions( obj ) {
6212 var i, singleField,
6213 fieldId = obj.getAttribute( 'data-fid' ),
6214 fieldType = obj.getAttribute( 'data-type' ),
6215 allFieldSettings = document.querySelectorAll( '.frm-single-settings:not(.frm_hidden)' );
6216
6217 for ( i = 0; i < allFieldSettings.length; i++ ) {
6218 allFieldSettings[i].classList.add( 'frm_hidden' );
6219 }
6220
6221 singleField = document.getElementById( 'frm-single-settings-' + fieldId );
6222 moveFieldSettings( singleField );
6223
6224 if ( fieldType && 'quantity' === fieldType ) {
6225 popProductFields( jQuery( singleField ).find( '.frmjs_prod_field_opt' )[0]);
6226 }
6227
6228 singleField.classList.remove( 'frm_hidden' );
6229 document.getElementById( 'frm-options-panel-tab' ).click();
6230
6231 const editor = singleField.querySelector( '.wp-editor-area' );
6232 if ( editor ) {
6233 frmDom.wysiwyg.init(
6234 editor,
6235 { setupCallback: setupTinyMceEventHandlers }
6236 );
6237 }
6238 }
6239
6240 function setupTinyMceEventHandlers( editor ) {
6241 editor.on( 'Change', function() {
6242 handleTinyMceChange( editor );
6243 });
6244 }
6245
6246 function handleTinyMceChange( editor ) {
6247 if ( ! isTinyMceActive() || tinyMCE.activeEditor.isHidden() ) {
6248 return;
6249 }
6250
6251 editor.targetElm.value = editor.getContent();
6252 jQuery( editor.targetElm ).trigger( 'change' );
6253 }
6254
6255 function isTinyMceActive() {
6256 var activeSettings, wrapper;
6257
6258 activeSettings = document.querySelector( '.frm-single-settings:not(.frm_hidden)' );
6259 if ( ! activeSettings ) {
6260 return false;
6261 }
6262
6263 wrapper = activeSettings.querySelector( '.wp-editor-wrap' );
6264 return null !== wrapper && wrapper.classList.contains( 'tmce-active' );
6265 }
6266
6267 /**
6268 * Move the settings to the sidebar the first time they are changed or selected.
6269 * Keep the end marker at the end of the form.
6270 */
6271 function moveFieldSettings( singleField ) {
6272 if ( singleField === null ) {
6273 // The field may have not been loaded yet via ajax.
6274 return;
6275 }
6276
6277 var classes = singleField.parentElement.classList;
6278 if ( classes.contains( 'frm_field_box' ) || classes.contains( 'divider_section_only' ) ) {
6279 var endMarker = document.getElementById( 'frm-end-form-marker' );
6280 builderForm.insertBefore( singleField, endMarker );
6281 }
6282 }
6283
6284 function showEmailRow() {
6285 /*jshint validthis:true */
6286 var actionKey = jQuery( this ).closest( '.frm_form_action_settings' ).data( 'actionkey' );
6287 var rowType = this.getAttribute( 'data-emailrow' );
6288
6289 jQuery( '#frm_form_action_' + actionKey + ' .frm_' + rowType + '_row' ).fadeIn( 'slow' );
6290 jQuery( this ).fadeOut( 'slow' );
6291 }
6292
6293 function hideEmailRow() {
6294 /*jshint validthis:true */
6295 var actionBox = jQuery( this ).closest( '.frm_form_action_settings' ),
6296 rowType = this.getAttribute( 'data-emailrow' ),
6297 emailRowSelector = '.frm_' + rowType + '_row',
6298 emailButtonSelector = '.frm_' + rowType + '_button';
6299
6300 jQuery( actionBox ).find( emailButtonSelector ).fadeIn( 'slow' );
6301 jQuery( actionBox ).find( emailRowSelector ).fadeOut( 'slow', function() {
6302 jQuery( actionBox ).find( emailRowSelector + ' input' ).val( '' );
6303 });
6304 }
6305
6306 function showEmailWarning() {
6307 /*jshint validthis:true */
6308 var actionBox = jQuery( this ).closest( '.frm_form_action_settings' ),
6309 emailRowSelector = '.frm_from_to_match_row',
6310 fromVal = actionBox.find( 'input[name$="[post_content][from]"]' ).val(),
6311 toVal = actionBox.find( 'input[name$="[post_content][email_to]"]' ).val();
6312
6313 if ( fromVal === toVal ) {
6314 jQuery( actionBox ).find( emailRowSelector ).fadeIn( 'slow' );
6315 } else {
6316 jQuery( actionBox ).find( emailRowSelector ).fadeOut( 'slow' );
6317 }
6318 }
6319
6320 function checkActiveAction( type ) {
6321 const actionTriggers = document.querySelectorAll( '.frm_' + type + '_action' );
6322
6323 if ( isAtLimitForActionType( type ) ) {
6324 const addAlreadyUsedClass = getLimitForActionType( type ) > 0;
6325 markActionTriggersInactive( actionTriggers, addAlreadyUsedClass );
6326 return;
6327 }
6328
6329 markActionTriggersActive( actionTriggers );
6330 }
6331
6332 function markActionTriggersActive( triggers ) {
6333 triggers.forEach(
6334 trigger => {
6335 trigger.classList.remove( 'frm_inactive_action', 'frm_already_used' );
6336 trigger.classList.add( 'frm_active_action' );
6337 }
6338 );
6339 }
6340
6341 function markActionTriggersInactive( triggers, addAlreadyUsedClass ) {
6342 triggers.forEach(
6343 trigger => {
6344 trigger.classList.remove( 'frm_active_action' );
6345 trigger.classList.add( 'frm_inactive_action' );
6346 if ( addAlreadyUsedClass ) {
6347 trigger.classList.add( 'frm_already_used' );
6348 }
6349 }
6350 );
6351 }
6352
6353 function isAtLimitForActionType( type ) {
6354 let atLimit = getNumberOfActionsForType( type ) >= getLimitForActionType( type );
6355
6356 const hookName = 'frm_action_at_limit';
6357 const hookArgs = { type };
6358 atLimit = wp.hooks.applyFilters( hookName, atLimit, hookArgs );
6359
6360 return atLimit;
6361 }
6362
6363 function getLimitForActionType( type ) {
6364 return parseInt( jQuery( '.frm_' + type + '_action' ).data( 'limit' ), 10 );
6365 }
6366
6367 function getNumberOfActionsForType( type ) {
6368 return jQuery( '.frm_single_' + type + '_settings' ).length;
6369 }
6370
6371 function onlyOneActionMessage() {
6372 infoModal( frm_admin_js.only_one_action );
6373 }
6374
6375 function addFormLogicRow() {
6376 /*jshint validthis:true */
6377 var id = jQuery( this ).data( 'emailkey' ),
6378 type = jQuery( this ).closest( '.frm_form_action_settings' ).find( '.frm_action_name' ).val(),
6379 formId = document.getElementById( 'form_id' ).value,
6380 logicRows = document.getElementById( 'frm_form_action_' + id ).querySelectorAll( '.frm_logic_row' );
6381 jQuery.ajax({
6382 type: 'POST', url: ajaxurl,
6383 data: {
6384 action: 'frm_add_form_logic_row',
6385 email_id: id,
6386 form_id: formId,
6387 meta_name: getNewRowId( logicRows, 'frm_logic_' + id + '_' ),
6388 type: type,
6389 nonce: frmGlobal.nonce
6390 },
6391 success: function( html ) {
6392 jQuery( document.getElementById( 'logic_link_' + id ) ).fadeOut( 'slow', function() {
6393 var $logicRow = jQuery( document.getElementById( 'frm_logic_row_' + id ) );
6394 $logicRow.append( html );
6395 $logicRow.parent( '.frm_logic_rows' ).fadeIn( 'slow' );
6396 });
6397 }
6398 });
6399 return false;
6400 }
6401
6402 function toggleSubmitLogic() {
6403 /*jshint validthis:true */
6404 if ( this.checked ) {
6405 addSubmitLogic();
6406 } else {
6407 jQuery( '.frm_logic_row_submit' ).remove();
6408 document.getElementById( 'frm_submit_logic_rows' ).style.display = 'none';
6409 }
6410 }
6411
6412 /**
6413 * Adds submit button Conditional Logic row and reveals submit button Conditional Logic
6414 *
6415 * @returns {boolean}
6416 */
6417 function addSubmitLogic() {
6418 /*jshint validthis:true */
6419 var formId = thisFormId,
6420 logicRows = document.getElementById( 'frm_submit_logic_row' ).querySelectorAll( '.frm_logic_row' );
6421 jQuery.ajax({
6422 type: 'POST',
6423 url: ajaxurl,
6424 data: {
6425 action: 'frm_add_submit_logic_row',
6426 form_id: formId,
6427 meta_name: getNewRowId( logicRows, 'frm_logic_submit_' ),
6428 nonce: frmGlobal.nonce
6429 },
6430 success: function( html ) {
6431 var $logicRow = jQuery( document.getElementById( 'frm_submit_logic_row' ) );
6432 $logicRow.append( html );
6433 $logicRow.parent( '.frm_submit_logic_rows' ).fadeIn( 'slow' );
6434 }
6435 });
6436 return false;
6437 }
6438
6439 /**
6440 * When the user selects a field for a submit condition, update corresponding options field accordingly.
6441 */
6442 function addSubmitLogicOpts() {
6443 var fieldOpt = jQuery( this );
6444 var fieldId = fieldOpt.find( ':selected' ).val();
6445
6446 if ( fieldId ) {
6447 var row = fieldOpt.data( 'row' );
6448 frmGetFieldValues( fieldId, 'submit', row, '', 'options[submit_conditions][hide_opt][]' );
6449 }
6450 }
6451
6452 function formatEmailSetting() {
6453 /*jshint validthis:true */
6454 /*var val = jQuery( this ).val();
6455 var email = val.match( /(\s[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/gi );
6456 if(email !== null && email.length) {
6457 //has email
6458 //TODO: add < > if they aren't there
6459 }*/
6460 }
6461
6462 function maybeShowFormMessages() {
6463 var header = document.getElementById( 'frm_messages_header' );
6464 if ( showFormMessages() ) {
6465 header.style.display = 'block';
6466 } else {
6467 header.style.display = 'none';
6468 }
6469 }
6470
6471 function showFormMessages() {
6472 var action = document.getElementById( 'success_action' );
6473 var selectedAction = action.options[action.selectedIndex].value;
6474 if ( selectedAction === 'message' ) {
6475 return true;
6476 }
6477
6478 var show = false;
6479 var editable = document.getElementById( 'editable' );
6480 if ( editable !== null ) {
6481 show = editable.checked && jQuery( document.getElementById( 'edit_action' ) ).val() === 'message';
6482 if ( ! show ) {
6483 show = isChecked( 'save_draft' );
6484 }
6485 }
6486 return show;
6487 }
6488
6489 function checkDupPost() {
6490 /*jshint validthis:true */
6491 var postField = jQuery( 'select.frm_single_post_field' );
6492 postField.css( 'border-color', '' );
6493 var $t = this;
6494 var v = jQuery( $t ).val();
6495 if ( v === '' || v === 'checkbox' ) {
6496 return false;
6497 }
6498 postField.each( function() {
6499 if ( jQuery( this ).val() === v && this.name !== $t.name ) {
6500 this.style.borderColor = 'red';
6501 jQuery( $t ).val( '' );
6502 infoModal( 'Oops. You have already used that field.' );
6503 return false;
6504 }
6505 });
6506 }
6507
6508 function togglePostContent() {
6509 /*jshint validthis:true */
6510 var v = jQuery( this ).val();
6511 if ( '' === v ) {
6512 jQuery( '.frm_post_content_opt, select.frm_dyncontent_opt' ).hide().val( '' );
6513 jQuery( '.frm_dyncontent_opt' ).hide();
6514 } else if ( 'post_content' === v ) {
6515 jQuery( '.frm_post_content_opt' ).show();
6516 jQuery( '.frm_dyncontent_opt' ).hide();
6517 jQuery( 'select.frm_dyncontent_opt' ).val( '' );
6518 } else {
6519 jQuery( '.frm_post_content_opt' ).hide().val( '' );
6520 jQuery( 'select.frm_dyncontent_opt, .frm_form_field.frm_dyncontent_opt' ).show();
6521 }
6522 }
6523
6524 function fillDyncontent() {
6525 /*jshint validthis:true */
6526 var v = jQuery( this ).val();
6527 var $dyn = jQuery( document.getElementById( 'frm_dyncontent' ) );
6528 if ( '' === v || 'new' === v ) {
6529 $dyn.val( '' );
6530 jQuery( '.frm_dyncontent_opt' ).show();
6531 } else {
6532 jQuery.ajax({
6533 type: 'POST', url: ajaxurl,
6534 data: {action: 'frm_display_get_content', id: v, nonce: frmGlobal.nonce},
6535 success: function( val ) {
6536 $dyn.val( val );
6537 jQuery( '.frm_dyncontent_opt' ).show();
6538 }
6539 });
6540 }
6541 }
6542
6543 function switchPostType() {
6544 /*jshint validthis:true */
6545 // update all rows of categories/taxonomies
6546 var curSelect, newSelect,
6547 catRows = document.getElementById( 'frm_posttax_rows' ).childNodes,
6548 postParentField = document.querySelector( '.frm_post_parent_field' ),
6549 postMenuOrderField = document.querySelector( '.frm_post_menu_order_field' ),
6550 postType = this.value;
6551
6552 // Get new category/taxonomy options
6553 jQuery.ajax({
6554 type: 'POST',
6555 url: ajaxurl,
6556 data: {
6557 action: 'frm_replace_posttax_options',
6558 post_type: postType,
6559 nonce: frmGlobal.nonce
6560 },
6561 success: function( html ) {
6562
6563 // Loop through each category row, and replace the first dropdown
6564 for ( i = 0; i < catRows.length; i++ ) {
6565 // Check if current element is a div
6566 if ( catRows[i].tagName !== 'DIV' ) {
6567 continue;
6568 }
6569
6570 // Get current category select
6571 curSelect = catRows[i].getElementsByTagName( 'select' )[0];
6572
6573 // Set up new select
6574 newSelect = document.createElement( 'select' );
6575 newSelect.innerHTML = html;
6576 newSelect.className = curSelect.className;
6577 newSelect.name = curSelect.name;
6578
6579 // Replace the old select with the new select
6580 catRows[i].replaceChild( newSelect, curSelect );
6581 }
6582 }
6583 });
6584
6585 // Get new post parent option.
6586 if ( postParentField ) {
6587 getActionOption(
6588 postParentField,
6589 postType,
6590 'frm_get_post_parent_option',
6591 function( response, optName ) {
6592 // The replaced string is declared in FrmProFormActionController::ajax_get_post_menu_order_option() in the pro version.
6593 postParentField.querySelector( '.frm_post_parent_opt_wrapper' ).innerHTML = response.replaceAll( 'REPLACETHISNAME', optName );
6594 frmDom.autocomplete.initAutocomplete( 'page', postParentField );
6595 }
6596 );
6597 }
6598
6599 if ( postMenuOrderField ) {
6600 getActionOption( postMenuOrderField, postType, 'frm_should_use_post_menu_order_option' );
6601 }
6602 }
6603
6604 function getActionOption( field, postType, action, successHandler ) {
6605 const opt = field.querySelector( '.frm_autocomplete_value_input' ) || field.querySelector( 'select' ),
6606 optName = opt.getAttribute( 'name' );
6607
6608 jQuery.ajax({
6609 url: ajaxurl,
6610 method: 'POST',
6611 data: {
6612 action: action,
6613 post_type: postType,
6614 _wpnonce: frmGlobal.nonce
6615 },
6616 success: response => {
6617 if ( 'string' !== typeof response ) {
6618 console.error( response );
6619 return;
6620 }
6621
6622 if ( '0' === response ) {
6623 // This post type does not support this field.
6624 field.classList.add( 'frm_hidden' );
6625 field.value = '';
6626 return;
6627 }
6628
6629 field.classList.remove( 'frm_hidden' );
6630
6631 if ( 'function' === typeof successHandler ) {
6632 successHandler( response, optName );
6633 }
6634 },
6635 error: response => console.error( response )
6636 });
6637 }
6638
6639 function addPosttaxRow() {
6640 /*jshint validthis:true */
6641 addPostRow( 'tax', this );
6642 }
6643
6644 function addPostmetaRow() {
6645 /*jshint validthis:true */
6646 addPostRow( 'meta', this );
6647 }
6648
6649 function addPostRow( type, button ) {
6650 var name,
6651 id = jQuery( 'input[name="id"]' ).val(),
6652 settings = jQuery( button ).closest( '.frm_form_action_settings' ),
6653 key = settings.data( 'actionkey' ),
6654 postType = settings.find( '.frm_post_type' ).val(),
6655 metaName = 0,
6656 postTypeRows = document.querySelectorAll( '.frm_post' + type + '_row' );
6657
6658 if ( postTypeRows.length ) {
6659 name = postTypeRows[ postTypeRows.length - 1 ].id.replace( 'frm_post' + type + '_', '' );
6660 if ( isNumeric( name ) ) {
6661 metaName = 1 + parseInt( name, 10 );
6662 } else {
6663 metaName = 1;
6664 }
6665 }
6666
6667 jQuery.ajax({
6668 type: 'POST', url: ajaxurl,
6669 data: {
6670 action: 'frm_add_post' + type + '_row',
6671 form_id: id,
6672 meta_name: metaName,
6673 tax_key: metaName,
6674 post_type: postType,
6675 action_key: key,
6676 nonce: frmGlobal.nonce
6677 },
6678 success: function( html ) {
6679 var cfOpts, optIndex;
6680 jQuery( document.getElementById( 'frm_post' + type + '_rows' ) ).append( html );
6681 jQuery( '.frm_add_post' + type + '_row.button' ).hide();
6682
6683 if ( type === 'meta' ) {
6684 jQuery( '.frm_name_value' ).show();
6685 cfOpts = document.querySelectorAll( '.frm_toggle_cf_opts' );
6686 for ( optIndex = 0; optIndex < cfOpts.length - 1; ++optIndex ) {
6687 cfOpts[ optIndex ].style.display = 'none';
6688 }
6689 } else if ( type === 'tax' ) {
6690 jQuery( '.frm_posttax_labels' ).show();
6691 }
6692 }
6693 });
6694 }
6695
6696 function isNumeric( value ) {
6697 return ! isNaN( parseFloat( value ) ) && isFinite( value );
6698 }
6699
6700 function getMetaValue( id, metaName ) {
6701 var newMeta = metaName;
6702 if ( jQuery( document.getElementById( id + metaName ) ).length > 0 ) {
6703 newMeta = getMetaValue( id, metaName + 1 );
6704 }
6705 return newMeta;
6706 }
6707
6708 function changePosttaxRow() {
6709 /*jshint validthis:true */
6710 if ( ! jQuery( this ).closest( '.frm_posttax_row' ).find( '.frm_posttax_opt_list' ).length ) {
6711 return;
6712 }
6713
6714 jQuery( this ).closest( '.frm_posttax_row' ).find( '.frm_posttax_opt_list' ).html( '<div class="spinner frm_spinner" style="display:block"></div>' );
6715
6716 var postType = jQuery( this ).closest( '.frm_form_action_settings' ).find( 'select[name$="[post_content][post_type]"]' ).val(),
6717 actionKey = jQuery( this ).closest( '.frm_form_action_settings' ).data( 'actionkey' ),
6718 taxKey = jQuery( this ).closest( '.frm_posttax_row' ).attr( 'id' ).replace( 'frm_posttax_', '' ),
6719 metaName = jQuery( this ).val(),
6720 showExclude = jQuery( document.getElementById( taxKey + '_show_exclude' ) ).is( ':checked' ) ? 1 : 0,
6721 fieldId = jQuery( 'select[name$="[post_category][' + taxKey + '][field_id]"]' ).val(),
6722 id = jQuery( 'input[name="id"]' ).val();
6723
6724 jQuery.ajax({
6725 type: 'POST',
6726 url: ajaxurl,
6727 data: {
6728 action: 'frm_add_posttax_row',
6729 form_id: id,
6730 post_type: postType,
6731 tax_key: taxKey,
6732 action_key: actionKey,
6733 meta_name: metaName,
6734 field_id: fieldId,
6735 show_exclude: showExclude,
6736 nonce: frmGlobal.nonce
6737 },
6738 success: function( html ) {
6739 var $tax = jQuery( document.getElementById( 'frm_posttax_' + taxKey ) );
6740 $tax.replaceWith( html );
6741 }
6742 });
6743 }
6744
6745 function toggleCfOpts() {
6746 /*jshint validthis:true */
6747 var row = jQuery( this ).closest( '.frm_postmeta_row' );
6748 var cancel = row.find( '.frm_cancelnew' );
6749 var select = row.find( '.frm_enternew' );
6750 if ( row.find( 'select.frm_cancelnew' ).is( ':visible' ) ) {
6751 cancel.hide();
6752 select.show();
6753 } else {
6754 cancel.show();
6755 select.hide();
6756 }
6757
6758 row.find( 'input.frm_enternew, select.frm_cancelnew' ).val( '' );
6759 return false;
6760 }
6761
6762 function toggleFormOpts() {
6763 /*jshint validthis:true */
6764 var changedOpt = jQuery( this );
6765 var val = changedOpt.val();
6766 if ( changedOpt.attr( 'type' ) === 'checkbox' ) {
6767 if ( this.checked === false ) {
6768 val = '';
6769 }
6770 }
6771
6772 var toggleClass = changedOpt.data( 'toggleclass' );
6773 if ( val === '' ) {
6774 jQuery( '.' + toggleClass ).hide();
6775 } else {
6776 jQuery( '.' + toggleClass ).show();
6777 jQuery( '.hide_' + toggleClass + '_' + val ).hide();
6778 }
6779 }
6780
6781 function submitSettings() {
6782 /*jshint validthis:true */
6783 preFormSave( this );
6784 triggerSubmit( document.querySelector( '.frm_form_settings' ) );
6785 }
6786
6787 /* View Functions */
6788 function showCount() {
6789 /*jshint validthis:true */
6790 var value = jQuery( this ).val();
6791
6792 var $cont = document.getElementById( 'date_select_container' );
6793 var tab = document.getElementById( 'frm_listing_tab' );
6794 var label = tab.getAttribute( 'data-label' );
6795 if ( value === 'calendar' ) {
6796 jQuery( '.hide_dyncontent, .hide_single_content' ).removeClass( 'frm_hidden' );
6797 jQuery( '.limit_container' ).addClass( 'frm_hidden' );
6798 $cont.style.display = 'block';
6799 } else if ( value === 'dynamic' ) {
6800 jQuery( '.hide_dyncontent, .limit_container, .hide_single_content' ).removeClass( 'frm_hidden' );
6801 } else if ( value === 'one' ) {
6802 label = tab.getAttribute( 'data-one' );
6803 jQuery( '.hide_dyncontent, .limit_container, .hide_single_content' ).addClass( 'frm_hidden' );
6804 } else {
6805 jQuery( '.hide_dyncontent' ).addClass( 'frm_hidden' );
6806 jQuery( '.limit_container, .hide_single_content' ).removeClass( 'frm_hidden' );
6807 }
6808
6809 if ( value !== 'calendar' ) {
6810 $cont.style.display = 'none';
6811 }
6812 tab.innerHTML = label;
6813 }
6814
6815 function displayFormSelected() {
6816 /*jshint validthis:true */
6817 var formId = jQuery( this ).val();
6818 thisFormId = formId; // set the global form id
6819 if ( formId === '' ) {
6820 return;
6821 }
6822
6823 jQuery.ajax({
6824 type: 'POST',
6825 url: ajaxurl,
6826 data: {
6827 action: 'frm_get_cd_tags_box',
6828 form_id: formId,
6829 nonce: frmGlobal.nonce
6830 },
6831 success: function( html ) {
6832 jQuery( '#frm_adv_info .categorydiv' ).html( html );
6833 }
6834 });
6835
6836 jQuery.ajax({
6837 type: 'POST',
6838 url: ajaxurl,
6839 data: {
6840 action: 'frm_get_date_field_select',
6841 form_id: formId,
6842 nonce: frmGlobal.nonce
6843 },
6844 success: function( html ) {
6845 jQuery( document.getElementById( 'date_select_container' ) ).html( html );
6846 }
6847 });
6848 }
6849
6850 function clickTabsAfterAjax() {
6851 /*jshint validthis:true */
6852 var t = jQuery( this ).attr( 'href' );
6853 jQuery( this ).parent().addClass( 'tabs' ).siblings( 'li' ).removeClass( 'tabs' );
6854 jQuery( t ).show().siblings( '.tabs-panel' ).hide();
6855 return false;
6856 }
6857
6858 function clickContentTab() {
6859 /*jshint validthis:true */
6860 link = jQuery( this );
6861 var t = link.attr( 'href' );
6862 if ( typeof t === 'undefined' ) {
6863 return false;
6864 }
6865
6866 var c = t.replace( '#', '.' );
6867 link.closest( '.nav-tab-wrapper' ).find( 'a' ).removeClass( 'nav-tab-active' );
6868 link.addClass( 'nav-tab-active' );
6869 jQuery( '.nav-menu-content' ).not( t ).not( c ).hide();
6870 jQuery( t + ',' + c ).show();
6871
6872 return false;
6873 }
6874
6875 function addOrderRow() {
6876 var logicRows = document.getElementById( 'frm_order_options' ).querySelectorAll( '.frm_logic_rows div' );
6877 jQuery.ajax({
6878 type: 'POST',
6879 url: ajaxurl,
6880 data: {
6881 action: 'frm_add_order_row',
6882 form_id: thisFormId,
6883 order_key: getNewRowId( logicRows, 'frm_order_field_', 1 ),
6884 nonce: frmGlobal.nonce
6885 },
6886 success: function( html ) {
6887 jQuery( '#frm_order_options .frm_logic_rows' ).append( html ).show().prev( '.frm_add_order_row' ).hide();
6888 }
6889 });
6890 }
6891
6892 function addWhereRow() {
6893 var rowDivs = document.getElementById( 'frm_where_options' ).querySelectorAll( '.frm_logic_rows div' );
6894 jQuery.ajax({
6895 type: 'POST',
6896 url: ajaxurl,
6897 data: {
6898 action: 'frm_add_where_row',
6899 form_id: thisFormId,
6900 where_key: getNewRowId( rowDivs, 'frm_where_field_', 1 ),
6901 nonce: frmGlobal.nonce
6902 },
6903 success: function( html ) {
6904 jQuery( '#frm_where_options .frm_logic_rows' ).append( html ).show().prev( '.frm_add_where_row' ).hide();
6905 }
6906 });
6907 }
6908
6909 function insertWhereOptions() {
6910 /*jshint validthis:true */
6911 var value = this.value,
6912 whereKey = jQuery( this ).closest( '.frm_where_row' ).attr( 'id' ).replace( 'frm_where_field_', '' );
6913
6914 jQuery.ajax({
6915 type: 'POST',
6916 url: ajaxurl,
6917 data: {
6918 action: 'frm_add_where_options',
6919 where_key: whereKey,
6920 field_id: value,
6921 nonce: frmGlobal.nonce
6922 },
6923 success: function( html ) {
6924 jQuery( document.getElementById( 'where_field_options_' + whereKey ) ).html( html );
6925 }
6926 });
6927 }
6928
6929 function hideWhereOptions() {
6930 /*jshint validthis:true */
6931 var value = this.value,
6932 whereKey = jQuery( this ).closest( '.frm_where_row' ).attr( 'id' ).replace( 'frm_where_field_', '' );
6933
6934 if ( value === 'group_by' || value === 'group_by_newest' ) {
6935 document.getElementById( 'where_field_options_' + whereKey ).style.display = 'none';
6936 } else {
6937 document.getElementById( 'where_field_options_' + whereKey ).style.display = 'inline-block';
6938 }
6939 }
6940
6941 function setDefaultPostStatus() {
6942 var urlQuery = window.location.search.substring( 1 );
6943 if ( urlQuery.indexOf( 'action=edit' ) === -1 ) {
6944 document.getElementById( 'post-visibility-display' ).innerHTML = frm_admin_js.private_label;
6945 document.getElementById( 'hidden-post-visibility' ).value = 'private';
6946 document.getElementById( 'visibility-radio-private' ).checked = true;
6947 }
6948 }
6949
6950 /* Customization Panel */
6951 function insertCode( e ) {
6952 /*jshint validthis:true */
6953 e.preventDefault();
6954 insertFieldCode( jQuery( this ), this.getAttribute( 'data-code' ) );
6955 return false;
6956 }
6957
6958 function insertFieldCode( element, variable ) {
6959 var rich = false,
6960 elementId = element;
6961 if ( typeof element === 'object' ) {
6962 if ( element.hasClass( 'frm_noallow' ) ) {
6963 return;
6964 }
6965
6966 elementId = jQuery( element ).closest( '[data-fills]' ).attr( 'data-fills' );
6967 if ( typeof elementId === 'undefined' ) {
6968 elementId = element.closest( 'div' ).attr( 'class' );
6969 if ( typeof elementId !== 'undefined' ) {
6970 elementId = elementId.split( ' ' )[1];
6971 }
6972 }
6973 }
6974
6975 if ( typeof elementId === 'undefined' ) {
6976 var active = document.activeElement;
6977 if ( active.type === 'search' ) {
6978 // If the search field has focus, find the correct field.
6979 elementId = active.id.replace( '-search-input', '' );
6980 if ( elementId.match( /\d/gi ) === null ) {
6981 active = jQuery( '.frm-single-settings:visible .' + elementId );
6982 elementId = active.attr( 'id' );
6983 }
6984 } else {
6985 elementId = active.id;
6986 }
6987 }
6988
6989 if ( elementId ) {
6990 rich = jQuery( '#wp-' + elementId + '-wrap.wp-editor-wrap' ).length > 0;
6991 }
6992
6993 var contentBox = jQuery( document.getElementById( elementId ) );
6994 if ( typeof element.attr( 'data-shortcode' ) === 'undefined' && ( ! contentBox.length || typeof contentBox.attr( 'data-shortcode' ) === 'undefined' ) ) {
6995 // this helps to exclude those that don't want shortcode-like inserted content e.g. frm-pro's summary field
6996 var doShortcode = element.parents( 'ul.frm_code_list' ).attr( 'data-shortcode' );
6997 if ( doShortcode === 'undefined' || doShortcode !== 'no' ) {
6998 variable = '[' + variable + ']';
6999 }
7000 }
7001
7002 if ( rich ) {
7003 wpActiveEditor = elementId;
7004 send_to_editor( variable );
7005 return;
7006 }
7007
7008 if ( ! contentBox.length ) {
7009 return false;
7010 }
7011
7012 if ( variable === '[default-html]' || variable === '[default-plain]' ) {
7013 var p = 0;
7014 if ( variable === '[default-plain]' ) {
7015 p = 1;
7016 }
7017 jQuery.ajax({
7018 type: 'POST', url: ajaxurl,
7019 data: {
7020 action: 'frm_get_default_html',
7021 form_id: jQuery( 'input[name="id"]' ).val(),
7022 plain_text: p,
7023 nonce: frmGlobal.nonce
7024 },
7025 success: function( msg ) {
7026 insertContent( contentBox, msg );
7027 }
7028 });
7029 } else {
7030 variable = maybeAddSanitizeUrlToShortcodeVariable( variable, element, contentBox );
7031 insertContent( contentBox, variable );
7032 }
7033 return false;
7034 }
7035
7036 function maybeAddSanitizeUrlToShortcodeVariable( variable, element, contentBox ) {
7037 if ( 'object' !== typeof element || ! ( element instanceof jQuery ) || 'success_url' !== contentBox[0].id ) {
7038 return variable;
7039 }
7040
7041 element = element[0];
7042 if ( ! element.closest( '#frm-insert-fields-box' ) ) {
7043 // Only add sanitize_url=1 to field shortcodes.
7044 return variable;
7045 }
7046
7047 if ( ! element.parentNode.classList.contains( 'frm_insert_url' ) ) {
7048 variable = variable.replace( ']', ' sanitize_url=1]' );
7049 }
7050
7051 return variable;
7052 }
7053
7054 function insertContent( contentBox, variable ) {
7055 if ( document.selection ) {
7056 contentBox[0].focus();
7057 document.selection.createRange().text = variable;
7058 } else {
7059 obj = contentBox[0];
7060 var e = obj.selectionEnd;
7061
7062 variable = maybeFormatInsertedContent( contentBox, variable, obj.selectionStart, e );
7063
7064 obj.value = obj.value.substr( 0, obj.selectionStart ) + variable + obj.value.substr( obj.selectionEnd, obj.value.length );
7065 var s = e + variable.length;
7066 obj.focus();
7067 obj.setSelectionRange( s, s );
7068 }
7069 triggerChange( contentBox );
7070 }
7071
7072 function maybeFormatInsertedContent( input, textToInsert, selectionStart, selectionEnd ) {
7073 var separator = input.data( 'sep' );
7074 if ( undefined === separator ) {
7075 return textToInsert;
7076 }
7077
7078 var value = input.val();
7079
7080 if ( ! value.trim().length ) {
7081 return textToInsert;
7082 }
7083
7084 var startPattern = new RegExp( separator + '\\s*$' );
7085 var endPattern = new RegExp( '^\\s*' + separator );
7086
7087 if ( value.substr( 0, selectionStart ).trim().length && false === startPattern.test( value.substr( 0, selectionStart ) ) ) {
7088 textToInsert = separator + textToInsert;
7089 }
7090
7091 if ( value.substr( selectionEnd, value.length ).trim().length && false === endPattern.test( value.substr( selectionEnd, value.length ) ) ) {
7092 textToInsert += separator;
7093 }
7094
7095 return textToInsert;
7096 }
7097
7098 function resetLogicBuilder() {
7099 /*jshint validthis:true */
7100 var id = document.getElementById( 'frm-id-condition' ),
7101 key = document.getElementById( 'frm-key-condition' );
7102
7103 if ( this.checked ) {
7104 id.classList.remove( 'frm_hidden' );
7105 key.classList.add( 'frm_hidden' );
7106 triggerEvent( key, 'change' );
7107 } else {
7108 id.classList.add( 'frm_hidden' );
7109 key.classList.remove( 'frm_hidden' );
7110 triggerEvent( id, 'change' );
7111 }
7112 }
7113
7114 function setLogicExample() {
7115 var field, code,
7116 idKey = document.getElementById( 'frm-id-key-condition' ).checked ? 'frm-id-condition' : 'frm-key-condition',
7117 is = document.getElementById( 'frm-is-condition' ).value,
7118 text = document.getElementById( 'frm-text-condition' ).value,
7119 result = document.getElementById( 'frm-insert-condition' );
7120
7121 idKey = document.getElementById( idKey );
7122 field = idKey.options[idKey.selectedIndex].value;
7123 code = 'if ' + field + ' ' + is + '="' + text + '"]';
7124 result.setAttribute( 'data-code', code + frm_admin_js.conditional_text + '[/if ' + field );
7125 result.innerHTML = '[' + code + '[/if ' + field + ']';
7126 }
7127
7128 function showBuilderModal() {
7129 /*jshint validthis:true */
7130 var moreIcon = getIconForInput( this );
7131 showInlineModal( moreIcon, this );
7132 }
7133
7134 function maybeShowModal( input ) {
7135 var moreIcon;
7136 if ( input.parentNode.parentNode.classList.contains( 'frm_has_shortcodes' ) ) {
7137 hideShortcodes();
7138 moreIcon = getIconForInput( input );
7139 if ( moreIcon.tagName === 'use' ) {
7140 moreIcon = moreIcon.firstElementChild;
7141 if ( moreIcon.getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' ).indexOf( 'frm_close_icon' ) === -1 ) {
7142 showShortcodeBox( moreIcon, 'nofocus' );
7143 }
7144 } else if ( ! moreIcon.classList.contains( 'frm_close_icon' ) ) {
7145 showShortcodeBox( moreIcon, 'nofocus' );
7146 }
7147 }
7148 }
7149
7150 function showShortcodes( e ) {
7151 /*jshint validthis:true */
7152 e.preventDefault();
7153 e.stopPropagation();
7154
7155 showShortcodeBox( this );
7156 }
7157
7158 function showShortcodeBox( moreIcon, shouldFocus ) {
7159 var pos = moreIcon.getBoundingClientRect(),
7160 input = getInputForIcon( moreIcon ),
7161 box = document.getElementById( 'frm_adv_info' ),
7162 classes = moreIcon.className,
7163 parentPos = box.parentElement.getBoundingClientRect();
7164
7165 if ( moreIcon.tagName === 'svg' ) {
7166 moreIcon = moreIcon.firstElementChild;
7167 }
7168 if ( moreIcon.tagName === 'use' ) {
7169 classes = moreIcon.getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' );
7170 }
7171
7172 if ( classes.indexOf( 'frm_close_icon' ) !== -1 ) {
7173 hideShortcodes( box );
7174 } else {
7175 box.style.top = ( pos.top - parentPos.top + 32 ) + 'px';
7176 box.style.left = ( pos.left - parentPos.left - 257 ) + 'px';
7177
7178 jQuery( '.frm_code_list a' ).removeClass( 'frm_noallow' );
7179 if ( input.classList.contains( 'frm_not_email_to' ) ) {
7180 jQuery( '#frm-insert-fields-box .frm_code_list li:not(.show_frm_not_email_to) a' ).addClass( 'frm_noallow' );
7181 } else if ( input.classList.contains( 'frm_not_email_subject' ) ) {
7182 jQuery( '.frm_code_list li.hide_frm_not_email_subject a' ).addClass( 'frm_noallow' );
7183 }
7184
7185 box.setAttribute( 'data-fills', input.id );
7186 box.style.display = 'block';
7187
7188 if ( moreIcon.tagName === 'use' ) {
7189 moreIcon.setAttributeNS( 'http://www.w3.org/1999/xlink', 'href', '#frm_close_icon' );
7190 } else {
7191 moreIcon.className = classes.replace( 'frm_more_horiz_solid_icon', 'frm_close_icon' );
7192 }
7193
7194 if ( shouldFocus !== 'nofocus' ) {
7195 input.focus();
7196 }
7197 }
7198 }
7199
7200 function fieldUpdated() {
7201 if ( ! fieldsUpdated ) {
7202 fieldsUpdated = 1;
7203 window.addEventListener( 'beforeunload', confirmExit );
7204 }
7205 }
7206
7207 function buildSubmittedNoAjax() {
7208 // set fieldsUpdated to 0 to avoid the unsaved changes pop up
7209 fieldsUpdated = 0;
7210 }
7211
7212 function settingsSubmitted() {
7213 // set fieldsUpdated to 0 to avoid the unsaved changes pop up
7214 fieldsUpdated = 0;
7215 }
7216
7217 function saveAndReloadSettings() {
7218 var page, form;
7219 page = document.getElementById( 'form_settings_page' );
7220 if ( null !== page ) {
7221 form = page.querySelector( 'form.frm_form_settings' );
7222 if ( null !== form ) {
7223 fieldsUpdated = 0;
7224 form.submit();
7225 }
7226 }
7227 }
7228
7229 function saveAndReloadFormBuilder() {
7230 document.getElementById( 'frm_submit_side_top' ).click();
7231 }
7232
7233 function confirmExit( event ) {
7234 if ( fieldsUpdated ) {
7235 event.preventDefault();
7236 event.returnValue = '';
7237 }
7238 }
7239
7240 function bindClickForDialogClose( $modal ) {
7241 const closeModal = function() {
7242 $modal.dialog( 'close' );
7243 };
7244 jQuery( '.ui-widget-overlay' ).on( 'click', closeModal );
7245 $modal.on( 'click', 'a.dismiss', closeModal );
7246 }
7247
7248 function triggerNewFormModal( event ) {
7249 var $modal,
7250 dismiss = document.getElementById( 'frm_new_form_modal' ).querySelector( 'a.dismiss' );
7251
7252 if ( typeof event !== 'undefined' ) {
7253 event.preventDefault();
7254 }
7255
7256 dismiss.setAttribute( 'tabindex', -1 );
7257
7258 $modal = initModal( '#frm_new_form_modal', '600px' );
7259 offsetModalY( $modal, '50px' );
7260 $modal.attr( 'frm-page', 'create' );
7261 $modal.find( '#template-search-input' ).val( '' ).trigger( 'change' );
7262 $modal.dialog( 'open' );
7263
7264 dismiss.removeAttribute( 'tabindex' );
7265 bindClickForDialogClose( $modal );
7266
7267 addApplicationsToNewFormModal( $modal.get( 0 ) );
7268 }
7269
7270 function addApplicationsToNewFormModal( modal ) {
7271 if ( modal.querySelector( '.frm-ready-made-solution' ) ) {
7272 // Avoid adding duplicates if the modal is opened multiple times.
7273 return;
7274 }
7275
7276 if ( ! frmGlobal.canAccessApplicationDashboard ) {
7277 // User does not have privileges to see Ready Made Solutions.
7278 return;
7279 }
7280
7281 doJsonFetch( 'get_applications_data&view=templates' ).then( addTemplatesOnFetchSuccess );
7282
7283 const categoryList = modal.querySelector( 'ul.frm-categories-list' );
7284
7285 function addTemplatesOnFetchSuccess( data ) {
7286 data.templates.forEach( addTemplateToCategoryList );
7287 }
7288
7289 function addTemplateToCategoryList( template ) {
7290 categoryList.insertBefore( getReadyMadeSolution( template ), categoryList.firstChild );
7291 }
7292
7293 function getReadyMadeSolution( template ) {
7294 const image = tag( 'img' );
7295 const thumbnailFolderUrl = frmGlobal.url + '/images/applications/thumbnails/';
7296 const filenameToUse = template.hasLiteThumbnail ? template.key + '.png' : 'placeholder.svg';
7297 image.setAttribute( 'src', thumbnailFolderUrl + filenameToUse );
7298
7299 const imageWrapper = div({ child: image });
7300 imageWrapper.style.textAlign = 'center';
7301
7302 return tag(
7303 'li',
7304 {
7305 className: 'frm-searchable-template frm-ready-made-solution',
7306 children: [
7307 imageWrapper,
7308 div({
7309 children: [
7310 span( __( 'Ready Made Solution', 'formidable' ) ),
7311 tag( 'h3', template.name ),
7312 a({
7313 text: __( 'Check all applications', 'formidable' ),
7314 href: frmGlobal.applicationsUrl
7315 })
7316 ]
7317 }),
7318 div({
7319 className: 'frm-hover-icons',
7320 child: a({
7321 child: svg({ href: '#frm_plus_icon' }),
7322 href: frmGlobal.applicationsUrl + '&triggerViewApplicationModal=1&template=' + template.key
7323 })
7324 })
7325 ]
7326 }
7327 );
7328 }
7329 }
7330
7331 function offsetModalY( $modal, amount ) {
7332 const position = {
7333 my: 'top',
7334 at: 'top+' + amount,
7335 of: window
7336 };
7337 $modal.dialog( 'option', 'position', position );
7338 }
7339
7340 /**
7341 * Get the input box for the selected ... icon.
7342 */
7343 function getInputForIcon( moreIcon ) {
7344 var input = moreIcon.nextElementSibling;
7345 if ( input !== null && input.tagName !== 'INPUT' && input.tagName !== 'TEXTAREA' ) {
7346 // Workaround for 1Password.
7347 input = input.nextElementSibling;
7348 }
7349 return input;
7350 }
7351
7352 /**
7353 * Get the ... icon for the selected input box.
7354 */
7355 function getIconForInput( input ) {
7356 var moreIcon = input.previousElementSibling;
7357 if ( moreIcon !== null && moreIcon.tagName !== 'I' && moreIcon.tagName !== 'svg' ) {
7358 moreIcon = moreIcon.previousElementSibling;
7359 }
7360 return moreIcon;
7361 }
7362
7363 function hideShortcodes( box ) {
7364 var i, u, closeIcons, closeSvg;
7365 if ( typeof box === 'undefined' ) {
7366 box = document.getElementById( 'frm_adv_info' );
7367 if ( box === null ) {
7368 return;
7369 }
7370 }
7371
7372 if ( document.getElementById( 'frm_dyncontent' ) !== null ) {
7373 // Don't run when in the sidebar.
7374 return;
7375 }
7376
7377 box.style.display = 'none';
7378
7379 closeIcons = document.querySelectorAll( '.frm-show-box.frm_close_icon' );
7380 for ( i = 0; i < closeIcons.length; i++ ) {
7381 closeIcons[i].classList.remove( 'frm_close_icon' );
7382 closeIcons[i].classList.add( 'frm_more_horiz_solid_icon' );
7383 }
7384
7385 closeSvg = document.querySelectorAll( '.frm_has_shortcodes use' );
7386 for ( u = 0; u < closeSvg.length; u++ ) {
7387 if ( closeSvg[u].getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' ) === '#frm_close_icon' ) {
7388 closeSvg[u].setAttributeNS( 'http://www.w3.org/1999/xlink', 'href', '#frm_more_horiz_solid_icon' );
7389 }
7390 }
7391 }
7392
7393 function initToggleShortcodes() {
7394 if ( typeof tinymce !== 'object' ) {
7395 return;
7396 }
7397
7398 DOM = tinymce.DOM;
7399 if ( typeof DOM.events !== 'undefined' && typeof DOM.events.add !== 'undefined' ) {
7400 DOM.events.add( DOM.select( '.wp-editor-wrap' ), 'mouseover', function() {
7401 if ( jQuery( '*:focus' ).length > 0 ) {
7402 return;
7403 }
7404 if ( this.id ) {
7405 toggleAllowedShortcodes( this.id.slice( 3, -5 ), 'focusin' );
7406 }
7407 });
7408 DOM.events.add( DOM.select( '.wp-editor-wrap' ), 'mouseout', function() {
7409 if ( jQuery( '*:focus' ).length > 0 ) {
7410 return;
7411 }
7412 if ( this.id ) {
7413 toggleAllowedShortcodes( this.id.slice( 3, -5 ), 'focusin' );
7414 }
7415 });
7416 } else {
7417 jQuery( '#frm_dyncontent' ).on( 'mouseover mouseout', '.wp-editor-wrap', function() {
7418 if ( jQuery( '*:focus' ).length > 0 ) {
7419 return;
7420 }
7421 if ( this.id ) {
7422 toggleAllowedShortcodes( this.id.slice( 3, -5 ), 'focusin' );
7423 }
7424 });
7425 }
7426 }
7427
7428 function toggleAllowedShortcodes( id ) {
7429 var c, clickedID;
7430 if ( typeof id === 'undefined' ) {
7431 id = '';
7432 }
7433 c = id;
7434
7435 if ( id.indexOf( '-search-input' ) !== -1 ) {
7436 return;
7437 }
7438
7439 if ( id !== '' ) {
7440 var $ele = jQuery( document.getElementById( id ) );
7441 if ( $ele.attr( 'class' ) && id !== 'wpbody-content' && id !== 'content' && id !== 'dyncontent' && id !== 'success_msg' ) {
7442 var d = $ele.attr( 'class' ).split( ' ' )[0];
7443 if ( d === 'frm_long_input' || d === 'frm_98_width' || typeof d === 'undefined' ) {
7444 d = '';
7445 } else {
7446 id = d.trim();
7447 }
7448 c = c + ' ' + d;
7449 c = c.replace( 'widefat', '' ).replace( 'frm_with_left_label', '' );
7450 }
7451 }
7452
7453 jQuery( '#frm-insert-fields-box,#frm-conditionals,#frm-adv-info-tab,#frm-dynamic-values' ).attr( 'data-fills', c.trim() );
7454 var a = [
7455 'content', 'wpbody-content', 'dyncontent', 'success_url',
7456 'success_msg', 'edit_msg', 'frm_dyncontent', 'frm_not_email_message',
7457 'frm_not_email_subject'
7458 ];
7459 var b = [
7460 'before_content', 'after_content', 'frm_not_email_to',
7461 'dyn_default_value'
7462 ];
7463
7464 if ( jQuery.inArray( id, a ) >= 0 ) {
7465 jQuery( '.frm_code_list a' ).removeClass( 'frm_noallow' ).addClass( 'frm_allow' );
7466 jQuery( '.frm_code_list a.hide_' + id ).addClass( 'frm_noallow' ).removeClass( 'frm_allow' );
7467 } else if ( jQuery.inArray( id, b ) >= 0 ) {
7468 jQuery( '.frm_code_list:not(.frm-dropdown-menu) a:not(.show_' + id + ')' ).addClass( 'frm_noallow' ).removeClass( 'frm_allow' );
7469 jQuery( '.frm_code_list a.show_' + id ).removeClass( 'frm_noallow' ).addClass( 'frm_allow' );
7470 } else {
7471 jQuery( '.frm_code_list:not(.frm-dropdown-menu) a' ).addClass( 'frm_noallow' ).removeClass( 'frm_allow' );
7472 }
7473
7474 // Automatically select a tab.
7475 if ( id === 'dyn_default_value' ) {
7476 clickedID = 'frm_dynamic_values';
7477 document.getElementById( clickedID + '_tab' ).click();
7478 jQuery( '#' + clickedID.replace( /_/g, '-' ) + ' .frm_show_inactive' ).addClass( 'frm_hidden' );
7479 jQuery( '#' + clickedID.replace( /_/g, '-' ) + ' .frm_show_active' ).removeClass( 'frm_hidden' );
7480 }
7481 }
7482
7483 function toggleAllowedHTML( input ) {
7484 var b,
7485 id = input.id;
7486 if ( typeof id === 'undefined' || id.indexOf( '-search-input' ) !== -1 ) {
7487 return;
7488 }
7489
7490 jQuery( '#frm-adv-info-tab' ).attr( 'data-fills', id.trim() );
7491 if ( input.classList.contains( 'field_custom_html' ) ) {
7492 id = 'field_custom_html';
7493 }
7494
7495 b = [ 'after_html', 'before_html', 'submit_html', 'field_custom_html' ];
7496 if ( jQuery.inArray( id, b ) >= 0 ) {
7497 jQuery( '.frm_code_list li:not(.show_' + id + ')' ).addClass( 'frm_hidden' );
7498 jQuery( '.frm_code_list li.show_' + id ).removeClass( 'frm_hidden' );
7499 }
7500 }
7501
7502 function toggleKeyID( switchTo, e ) {
7503 e.stopPropagation();
7504 jQuery( '.frm_code_list .frmids, .frm_code_list .frmkeys' ).addClass( 'frm_hidden' );
7505 jQuery( '.frm_code_list .' + switchTo ).removeClass( 'frm_hidden' );
7506 jQuery( '.frmids, .frmkeys' ).removeClass( 'current' );
7507 jQuery( '.' + switchTo ).addClass( 'current' );
7508 }
7509
7510 /* Styling */
7511 function setPosClass() {
7512 /*jshint validthis:true */
7513 var value = this.value;
7514 if ( value === 'none' ) {
7515 value = 'top';
7516 } else if ( value === 'no_label' ) {
7517 value = 'none';
7518 }
7519
7520 document.querySelectorAll( '.frm_pos_container' ).forEach( container => {
7521 // Fields that support floating label should have a directly child input/textarea/select.
7522 const input = container.querySelector( ':scope > input, :scope > select, :scope > textarea' );
7523
7524 if ( 'inside' === value && ! input ) {
7525 value = 'top';
7526 }
7527
7528 container.classList.remove( 'frm_top_container', 'frm_left_container', 'frm_right_container', 'frm_none_container', 'frm_inside_container' );
7529 container.classList.add( 'frm_' + value + '_container' );
7530
7531 if ( 'inside' === value ) {
7532 checkFloatingLabelsForStyles( input, container );
7533 }
7534 });
7535 }
7536
7537 function checkFloatingLabelsForStyles( input, container ) {
7538 if ( ! container ) {
7539 container = input.closest( '.frm_inside_container' );
7540 }
7541
7542 const shouldFloatTop = input.value || document.activeElement === input;
7543
7544 container.classList.toggle( 'frm_label_float_top', shouldFloatTop );
7545
7546 if ( 'SELECT' === input.tagName ) {
7547 const firstOpt = input.querySelector( 'option:first-child' );
7548
7549 if ( shouldFloatTop ) {
7550 if ( firstOpt.hasAttribute( 'data-label' ) ) {
7551 firstOpt.textContent = firstOpt.getAttribute( 'data-label' );
7552 firstOpt.removeAttribute( 'data-label' );
7553 }
7554 } else {
7555 if ( firstOpt.textContent ) {
7556 firstOpt.setAttribute( 'data-label', firstOpt.textContent );
7557 firstOpt.textContent = '';
7558 }
7559 }
7560 }
7561 }
7562
7563 function collapseAllSections() {
7564 jQuery( '.control-section.accordion-section.open' ).removeClass( 'open' );
7565 }
7566
7567 function textSquishCheck() {
7568 var size = document.getElementById( 'frm_field_font_size' ).value.replace( /\D/g, '' );
7569 var height = document.getElementById( 'frm_field_height' ).value.replace( /\D/g, '' );
7570 var paddingEntered = document.getElementById( 'frm_field_pad' ).value.split( ' ' );
7571 var paddingCount = paddingEntered.length;
7572
7573 // If too many or too few padding entries, leave now
7574 if ( paddingCount === 0 || paddingCount > 4 || height === '' ) {
7575 return;
7576 }
7577
7578 // Get the top and bottom padding from entered values
7579 var paddingTop = paddingEntered[0].replace( /\D/g, '' );
7580 var paddingBottom = paddingTop;
7581 if ( paddingCount >= 3 ) {
7582 paddingBottom = paddingEntered[2].replace( /\D/g, '' );
7583 }
7584
7585 // Check if there is enough space for text
7586 var textSpace = height - size - paddingTop - paddingBottom - 3;
7587 if ( textSpace < 0 ) {
7588 infoModal( frm_admin_js.css_invalid_size );
7589 }
7590 }
7591
7592 /* Global settings page */
7593 function loadSettingsTab( anchor ) {
7594 var holder = anchor.replace( '#', '' );
7595 var holderContainer = jQuery( '.frm_' + holder + '_ajax' );
7596 if ( holderContainer.length ) {
7597 jQuery.ajax({
7598 type: 'POST', url: ajaxurl,
7599 data: {
7600 'action': 'frm_settings_tab',
7601 'tab': holder.replace( '_settings', '' ),
7602 'nonce': frmGlobal.nonce
7603 },
7604 success: function( html ) {
7605 holderContainer.replaceWith( html );
7606 }
7607 });
7608 }
7609 }
7610
7611 function uninstallNow() {
7612 /*jshint validthis:true */
7613 if ( confirmLinkClick( this ) === true ) {
7614 jQuery( '.frm_uninstall .frm-wait' ).css( 'visibility', 'visible' );
7615 jQuery.ajax({
7616 type: 'POST',
7617 url: ajaxurl,
7618 data: 'action=frm_uninstall&nonce=' + frmGlobal.nonce,
7619 success: function( msg ) {
7620 jQuery( '.frm_uninstall' ).fadeOut( 'slow' );
7621 window.location = msg;
7622 }
7623 });
7624 }
7625 return false;
7626 }
7627
7628 function saveAddonLicense() {
7629 /*jshint validthis:true */
7630 var button = jQuery( this );
7631 var buttonName = this.name;
7632 var pluginSlug = this.getAttribute( 'data-plugin' );
7633 var action = buttonName.replace( 'edd_' + pluginSlug + '_license_', '' );
7634 var license = document.getElementById( 'edd_' + pluginSlug + '_license_key' ).value;
7635 jQuery.ajax({
7636 type: 'POST', url: ajaxurl, dataType: 'json',
7637 data: {action: 'frm_addon_' + action, license: license, plugin: pluginSlug, nonce: frmGlobal.nonce},
7638 success: function( msg ) {
7639 var thisRow = button.closest( '.edd_frm_license_row' );
7640 if ( action === 'deactivate' ) {
7641 license = '';
7642 document.getElementById( 'edd_' + pluginSlug + '_license_key' ).value = '';
7643 }
7644 thisRow.find( '.edd_frm_license' ).html( license );
7645 if ( msg.success === true ) {
7646 thisRow.find( '.frm_icon_font' ).removeClass( 'frm_hidden' );
7647 thisRow.find( 'div.alignleft' ).toggleClass( 'frm_hidden', 1000 );
7648 }
7649
7650 var messageBox = thisRow.find( '.frm_license_msg' );
7651 messageBox.html( msg.message );
7652 if ( msg.message !== '' ) {
7653 setTimeout( function() {
7654 messageBox.html( '' );
7655 }, 15000 );
7656 }
7657 }
7658 });
7659 }
7660
7661 /* Import/Export page */
7662
7663 function startFormMigration( event ) {
7664 event.preventDefault();
7665
7666 var checkedBoxes = jQuery( event.target ).find( 'input:checked' );
7667 if ( ! checkedBoxes.length ) {
7668 return;
7669 }
7670
7671 var ids = [];
7672 checkedBoxes.each( function( i ) {
7673 ids[i] = this.value;
7674 });
7675
7676 // Begin the import process.
7677 importForms( ids, event.target );
7678 }
7679
7680 /**
7681 * Begins the process of importing the forms.
7682 */
7683 function importForms( forms, targetForm ) {
7684
7685 // Hide the form select section.
7686 var $form = jQuery( targetForm ),
7687 $processSettings = $form.next( '.frm-importer-process' );
7688
7689 // Display total number of forms we have to import.
7690 $processSettings.find( '.form-total' ).text( forms.length );
7691 $processSettings.find( '.form-current' ).text( '1' );
7692
7693 $form.hide();
7694
7695 // Show processing status.
7696 // '.process-completed' might have been shown earlier during a previous import, so hide now.
7697 $processSettings.find( '.process-completed' ).hide();
7698 $processSettings.show();
7699
7700 // Create global import queue.
7701 s.importQueue = forms;
7702 s.imported = 0;
7703
7704 // Import the first form in the queue.
7705 importForm( $processSettings );
7706 }
7707
7708 /**
7709 * Imports a single form from the import queue.
7710 */
7711 function importForm( $processSettings ) {
7712 var formID = s.importQueue[0],
7713 provider = $processSettings.closest( '.welcome-panel-content' ).find( 'input[name="slug"]' ).val(),
7714 data = {
7715 action: 'frm_import_' + provider,
7716 form_id: formID,
7717 nonce: frmGlobal.nonce
7718 };
7719
7720 // Trigger AJAX import for this form.
7721 jQuery.post( ajaxurl, data, function( res ) {
7722
7723 if ( res.success ) {
7724 var statusUpdate;
7725
7726 if ( res.data.error ) {
7727 statusUpdate = '<p>' + res.data.name + ': ' + res.data.msg + '</p>';
7728 } else {
7729 statusUpdate = '<p>Imported <a href="' + res.data.link + '" target="_blank">' + res.data.name + '</a></p>';
7730 }
7731
7732 $processSettings.find( '.status' ).prepend( statusUpdate );
7733 $processSettings.find( '.status' ).show();
7734
7735 // Remove this form ID from the queue.
7736 s.importQueue = jQuery.grep( s.importQueue, function( value ) {
7737 return value != formID;
7738 });
7739 s.imported++;
7740
7741 if ( s.importQueue.length === 0 ) {
7742 $processSettings.find( '.process-count' ).hide();
7743 $processSettings.find( '.forms-completed' ).text( s.imported );
7744 $processSettings.find( '.process-completed' ).show();
7745 } else {
7746 // Import next form in the queue.
7747 $processSettings.find( '.form-current' ).text( s.imported + 1 );
7748 importForm( $processSettings );
7749 }
7750 }
7751 });
7752 }
7753
7754 function validateExport( e ) {
7755 /*jshint validthis:true */
7756 e.preventDefault();
7757
7758 var s = false;
7759 var $exportForms = jQuery( 'input[name="frm_export_forms[]"]' );
7760
7761 if ( ! jQuery( 'input[name="frm_export_forms[]"]:checked' ).val() ) {
7762 $exportForms.closest( '.frm-table-box' ).addClass( 'frm_blank_field' );
7763 s = 'stop';
7764 }
7765
7766 var $exportType = jQuery( 'input[name="type[]"]' );
7767 if ( ! jQuery( 'input[name="type[]"]:checked' ).val() && $exportType.attr( 'type' ) === 'checkbox' ) {
7768 $exportType.closest( 'p' ).addClass( 'frm_blank_field' );
7769 s = 'stop';
7770 }
7771
7772 if ( s === 'stop' ) {
7773 return false;
7774 }
7775
7776 e.stopPropagation();
7777 this.submit();
7778 }
7779
7780 function removeExportError() {
7781 /*jshint validthis:true */
7782 var t = jQuery( this ).closest( '.frm_blank_field' );
7783 if ( typeof t === 'undefined' ) {
7784 return;
7785 }
7786
7787 var $thisName = this.name;
7788 if ( $thisName === 'type[]' && jQuery( 'input[name="type[]"]:checked' ).val() ) {
7789 t.removeClass( 'frm_blank_field' );
7790 } else if ( $thisName === 'frm_export_forms[]' && jQuery( this ).val() ) {
7791 t.removeClass( 'frm_blank_field' );
7792 }
7793
7794 }
7795
7796 function checkCSVExtension() {
7797 /*jshint validthis:true */
7798 var f = jQuery( this ).val();
7799 var re = /\.csv$/i;
7800 if ( f.match( re ) !== null ) {
7801 jQuery( '.show_csv' ).fadeIn();
7802 } else {
7803 jQuery( '.show_csv' ).fadeOut();
7804 }
7805 }
7806
7807 function checkExportTypes() {
7808 /*jshint validthis:true */
7809 var $dropdown = jQuery( this );
7810 var $selected = $dropdown.find( ':selected' );
7811 var s = $selected.data( 'support' );
7812
7813 var multiple = s.indexOf( '|' );
7814 jQuery( 'input[name="type[]"]' ).each( function() {
7815 this.checked = false;
7816 if ( s.indexOf( this.value ) >= 0 ) {
7817 this.disabled = false;
7818 if ( multiple === -1 ) {
7819 this.checked = true;
7820 }
7821 } else {
7822 this.disabled = true;
7823 }
7824 });
7825
7826 if ( $dropdown.val() === 'csv' ) {
7827 jQuery( '.csv_opts' ).show();
7828 jQuery( '.xml_opts' ).hide();
7829 } else {
7830 jQuery( '.csv_opts' ).hide();
7831 jQuery( '.xml_opts' ).show();
7832 }
7833
7834 var c = $selected.data( 'count' );
7835 var exportField = jQuery( 'input[name="frm_export_forms[]"]' );
7836 if ( c === 'single' ) {
7837 exportField.prop( 'multiple', false );
7838 exportField.prop( 'checked', false );
7839 } else {
7840 exportField.prop( 'multiple', true );
7841 exportField.prop( 'disabled', false );
7842 }
7843 }
7844
7845 function preventMultipleExport() {
7846 var type = jQuery( 'select[name=format]' ),
7847 selected = type.find( ':selected' ),
7848 count = selected.data( 'count' ),
7849 exportField = jQuery( 'input[name="frm_export_forms[]"]' );
7850
7851 if ( count === 'single' ) {
7852 // Disable all other fields to prevent multiple selections.
7853 if ( this.checked ) {
7854 exportField.prop( 'disabled', true );
7855 this.removeAttribute( 'disabled' );
7856 } else {
7857 exportField.prop( 'disabled', false );
7858 }
7859 } else {
7860 exportField.prop( 'disabled', false );
7861 }
7862 }
7863
7864 function initiateMultiselect() {
7865 jQuery( '.frm_multiselect' ).hide().each( frmDom.bootstrap.multiselect.init );
7866 }
7867
7868 /* Addons page */
7869 function installMultipleAddons( e ) {
7870 e.preventDefault();
7871 installOrActivate( this, 'frm_multiple_addons' );
7872 }
7873
7874 function activateAddon( e ) {
7875 e.preventDefault();
7876 installOrActivate( this, 'frm_activate_addon' );
7877 }
7878
7879 function installAddon( e ) {
7880 e.preventDefault();
7881 installOrActivate( this, 'frm_install_addon' );
7882 }
7883
7884 function installOrActivate( clicked, action ) {
7885 let button, plugin, el, message;
7886
7887 // Remove any leftover error messages, output an icon and get the plugin basename that needs to be activated.
7888 jQuery( '.frm-addon-error' ).remove();
7889 button = jQuery( clicked );
7890 plugin = button.attr( 'rel' );
7891 el = button.parent();
7892 message = el.parent().find( '.addon-status-label' );
7893
7894 button.addClass( 'frm_loading_button' );
7895
7896 // Process the Ajax to perform the activation.
7897 jQuery.ajax({
7898 url: ajaxurl,
7899 type: 'POST',
7900 async: true,
7901 cache: false,
7902 dataType: 'json',
7903 data: {
7904 action: action,
7905 nonce: frmGlobal.nonce,
7906 plugin: plugin
7907 },
7908 success: function( response ) {
7909 let saveAndReload;
7910
7911 if ( 'string' !== typeof response && 'string' === typeof response.message ) {
7912 if ( 'undefined' !== typeof response.saveAndReload ) {
7913 saveAndReload = response.saveAndReload;
7914 }
7915 response = response.message;
7916 }
7917
7918 const error = extractErrorFromAddOnResponse( response );
7919 if ( error ) {
7920 addonError( error, el, button );
7921 return;
7922 }
7923
7924 afterAddonInstall( response, button, message, el, saveAndReload );
7925 },
7926 error: function() {
7927 button.removeClass( 'frm_loading_button' );
7928 }
7929 });
7930 }
7931
7932 function installAddonWithCreds( e ) {
7933 // Prevent the default action, let the user know we are attempting to install again and go with it.
7934 e.preventDefault();
7935
7936 // Now let's make another Ajax request once the user has submitted their credentials.
7937 const proceed = jQuery( this );
7938 const el = proceed.parent().parent();
7939 const plugin = proceed.attr( 'rel' );
7940
7941 proceed.addClass( 'frm_loading_button' );
7942
7943 jQuery.ajax({
7944 url: ajaxurl,
7945 type: 'POST',
7946 async: true,
7947 cache: false,
7948 dataType: 'json',
7949 data: {
7950 action: 'frm_install_addon',
7951 nonce: frm_admin_js.nonce,
7952 plugin: plugin,
7953 hostname: el.find( '#hostname' ).val(),
7954 username: el.find( '#username' ).val(),
7955 password: el.find( '#password' ).val()
7956 },
7957 success: function( response ) {
7958 const error = extractErrorFromAddOnResponse( response );
7959 if ( error ) {
7960 addonError( error, el, proceed );
7961 return;
7962 }
7963
7964 afterAddonInstall( response, proceed, message, el );
7965 },
7966 error: function() {
7967 proceed.removeClass( 'frm_loading_button' );
7968 }
7969 });
7970 }
7971
7972 function afterAddonInstall( response, button, message, el, saveAndReload ) {
7973 const addonStatuses = document.querySelectorAll( '.frm-addon-status' );
7974 addonStatuses.forEach(
7975 addonStatus => {
7976 addonStatus.textContent = response;
7977 addonStatus.style.display = 'block';
7978 }
7979 );
7980
7981 // The Ajax request was successful, so let's update the output.
7982 button.css({ opacity: '0' });
7983 message.text( frm_admin_js.active );
7984
7985 document.querySelectorAll( '.frm-oneclick' ).forEach(
7986 oneClick => {
7987 oneClick.style.display = 'none';
7988 }
7989 );
7990
7991 jQuery( '#frm_upgrade_modal h2' ).hide();
7992 jQuery( '#frm_upgrade_modal .frm_lock_icon' ).addClass( 'frm_lock_open_icon' );
7993 jQuery( '#frm_upgrade_modal .frm_lock_icon use' ).attr( 'xlink:href', '#frm_lock_open_icon' );
7994
7995 // Proceed with CSS changes
7996 el.parent().removeClass( 'frm-addon-not-installed frm-addon-installed' ).addClass( 'frm-addon-active' );
7997 button.removeClass( 'frm_loading_button' );
7998
7999 // Maybe refresh import and SMTP pages
8000 const refreshPage = document.querySelectorAll( '.frm-admin-page-import, #frm-admin-smtp, #frm-welcome' );
8001 if ( refreshPage.length > 0 ) {
8002 window.location.reload();
8003 return;
8004 }
8005
8006 if ([ 'settings', 'form_builder' ].includes( saveAndReload ) ) {
8007 addonStatuses.forEach(
8008 addonStatus => {
8009 const inModal = null !== addonStatus.closest( '#frm_upgrade_modal' );
8010 addonStatus.appendChild( getSaveAndReloadSettingsOptions( saveAndReload, inModal ) );
8011 }
8012 );
8013 }
8014 }
8015
8016 function getSaveAndReloadSettingsOptions( saveAndReload, inModal ) {
8017 const className = 'frm-save-and-reload-options';
8018 const children = [ saveAndReloadSettingsButton( saveAndReload ) ];
8019 if ( inModal ) {
8020 children.push( closePopupButton() );
8021 }
8022 return div({ className, children });
8023 }
8024
8025 function saveAndReloadSettingsButton( saveAndReload ) {
8026 var button = document.createElement( 'button' );
8027 button.classList.add( 'frm-save-and-reload', 'button', 'button-primary', 'frm-button-primary' );
8028 button.textContent = __( 'Save and Reload', 'formidable' );
8029 button.addEventListener( 'click', () => {
8030 if ( saveAndReload === 'form_builder' ) {
8031 saveAndReloadFormBuilder();
8032 } else if ( saveAndReload === 'settings' ) {
8033 saveAndReloadSettings();
8034 }
8035 });
8036 return button;
8037 }
8038
8039 function closePopupButton() {
8040 var a = document.createElement( 'a' );
8041 a.setAttribute( 'href', '#' );
8042 a.classList.add( 'button', 'button-secondary', 'frm-button-secondary', 'dismiss' );
8043 a.textContent = __( 'Close', 'formidable' );
8044 return a;
8045 }
8046
8047 function extractErrorFromAddOnResponse( response ) {
8048 if ( typeof response !== 'string' ) {
8049 if ( typeof response.success !== 'undefined' && response.success ) {
8050 return false;
8051 }
8052
8053 if ( response.form ) {
8054 if ( jQuery( response.form ).is( '#message' ) ) {
8055 return {
8056 message: jQuery( response.form ).find( 'p' ).html()
8057 };
8058 }
8059 }
8060
8061 return response;
8062 }
8063
8064 return false;
8065 }
8066
8067 function addonError( response, el, button ) {
8068 if ( response.form ) {
8069 jQuery( '.frm-inline-error' ).remove();
8070 button.closest( '.frm-card' )
8071 .html( response.form )
8072 .css({ padding: 5 })
8073 .find( '#upgrade' )
8074 .attr( 'rel', button.attr( 'rel' ) )
8075 .on( 'click', installAddonWithCreds );
8076 } else {
8077 el.append( '<div class="frm-addon-error frm_error_style"><p><strong>' + response.message + '</strong></p></div>' );
8078 button.removeClass( 'frm_loading_button' );
8079 jQuery( '.frm-addon-error' ).delay( 4000 ).fadeOut();
8080 }
8081 }
8082
8083 /* Templates */
8084
8085 function initNewFormModal() {
8086 var installFormTrigger,
8087 activeHoverIcons,
8088 $modal,
8089 handleError,
8090 handleEmailAddressError,
8091 handleConfirmEmailAddressError,
8092 showFreeTemplatesForm,
8093 firstLockedTemplate,
8094 isShowFreeTemplatesFormFirst,
8095 url,
8096 urlParams;
8097
8098 url = new URL( window.location.href );
8099 urlParams = url.searchParams;
8100
8101 isShowFreeTemplatesFormFirst = urlParams.get( 'free-templates' );
8102
8103 jQuery( document ).on( 'click', '.frm-trigger-new-form-modal', triggerNewFormModal );
8104 $modal = initModal( '#frm_new_form_modal', '600px' );
8105
8106 if ( false === $modal ) {
8107 return;
8108 }
8109
8110 setTimeout(
8111 function() {
8112 $modal.get( 0 ).querySelector( '.postbox' ).style.display = 'block'; // Fixes pro issue #3508, prevent a conflict that hides the postbox in modal.
8113 },
8114 0
8115 );
8116
8117 installFormTrigger = document.createElement( 'a' );
8118 installFormTrigger.classList.add( 'frm-install-template', 'frm_hidden' );
8119 document.body.appendChild( installFormTrigger );
8120
8121 jQuery( '.frm-install-template' ).on( 'click', function( event ) {
8122 var $h3Clone = jQuery( this ).closest( 'li, td' ).find( 'h3' ).clone(),
8123 nameLabel = document.getElementById( 'frm_new_name' ),
8124 descLabel = document.getElementById( 'frm_new_desc' ),
8125 oldName;
8126
8127 $h3Clone.find( 'svg, .frm-plan-required-tag' ).remove();
8128 oldName = $h3Clone.html().trim();
8129
8130 event.preventDefault();
8131
8132 document.getElementById( 'frm_template_name' ).value = oldName;
8133 document.getElementById( 'frm_link' ).value = this.attributes.rel.value;
8134 document.getElementById( 'frm_action_type' ).value = 'frm_install_template';
8135 nameLabel.innerHTML = nameLabel.getAttribute( 'data-form' );
8136 descLabel.innerHTML = descLabel.getAttribute( 'data-form' );
8137 $modal.dialog( 'open' );
8138 });
8139
8140 jQuery( document ).on( 'submit', '#frm-new-template', installTemplate );
8141
8142 jQuery( document ).on( 'click', '.frm-hover-icons .frm-preview-form', function( event ) {
8143 var $li, link, iframe,
8144 container = document.getElementById( 'frm-preview-block' );
8145
8146 event.preventDefault();
8147
8148 $li = jQuery( this ).closest( 'li' );
8149 link = $li.attr( 'data-preview' );
8150
8151 if ( link.indexOf( ajaxurl ) > -1 ) {
8152 iframe = document.createElement( 'iframe' );
8153 iframe.src = link;
8154 iframe.height = '400';
8155 iframe.width = '100%';
8156 container.innerHTML = '';
8157 container.appendChild( iframe );
8158 } else {
8159 frmApiPreview( container, link );
8160 }
8161
8162 jQuery( '#frm-preview-title' ).text( getStrippedTemplateName( $li ) );
8163 $modal.attr( 'frm-page', 'preview' );
8164 activeHoverIcons = jQuery( this ).closest( '.frm-hover-icons' );
8165 });
8166
8167 jQuery( document ).on( 'click', 'li .frm-hover-icons .frm-create-form', function( event ) {
8168 var $li, name, link, action;
8169
8170 event.preventDefault();
8171
8172 $li = jQuery( this ).closest( 'li' );
8173
8174 if ( $li.is( '[data-href]' ) ) {
8175 window.location = $li.attr( 'data-href' );
8176 return;
8177 }
8178
8179 if ( $li.hasClass( 'frm-add-blank-form' ) ) {
8180 name = link = '';
8181 action = 'frm_install_form';
8182 } else if ( $li.is( '[data-rel]' ) ) {
8183 name = getStrippedTemplateName( $li );
8184 link = $li.attr( 'data-rel' );
8185 action = 'frm_install_template';
8186 } else {
8187 return;
8188 }
8189
8190 transitionToAddDetails( $modal, name, link, action );
8191 });
8192
8193 // Welcome page modals.
8194 jQuery( document ).on( 'click', '.frm-create-blank-form', function( event ) {
8195 event.preventDefault();
8196 jQuery( '.frm-trigger-new-form-modal' ).trigger( 'click' );
8197 transitionToAddDetails( $modal, '', '', 'frm_install_form' );
8198
8199 // Close the modal with the cancel button.
8200 jQuery( '.frm-modal-cancel.frm-back-to-all-templates' ).on( 'click', function() {
8201 jQuery( '.ui-widget-overlay' ).trigger( 'click' );
8202 });
8203 });
8204
8205 jQuery( document ).on( 'click', '.frm-featured-forms.frm-templates-list li [role="button"]:not(a), .frm-templates-list .accordion-section.open li [role="button"]:not(a)', function( event ) {
8206 var $hoverIcons, $trigger,
8207 $li = jQuery( this ).closest( 'li' ),
8208 triggerClass = $li.hasClass( 'frm-locked-template' ) ? 'frm-unlock-form' : 'frm-create-form';
8209
8210 $hoverIcons = $li.find( '.frm-hover-icons' );
8211 if ( ! $hoverIcons.length ) {
8212 $li.trigger( 'mouseover' );
8213 $hoverIcons = $li.find( '.frm-hover-icons' );
8214 $hoverIcons.hide();
8215 }
8216
8217 $trigger = $hoverIcons.find( '.' + triggerClass );
8218 $trigger.trigger( 'click' );
8219 });
8220
8221 jQuery( document ).on( 'click', 'li .frm-hover-icons .frm-delete-form', function( event ) {
8222 var $li,
8223 trigger;
8224
8225 event.preventDefault();
8226
8227 $li = jQuery( this ).closest( 'li' );
8228 $li.addClass( 'frm-deleting' );
8229 trigger = document.createElement( 'a' );
8230 trigger.setAttribute( 'href', '#' );
8231 trigger.setAttribute( 'data-id', $li.attr( 'data-formid' ) );
8232 $li.attr( 'id', 'frm-template-custom-' + $li.attr( 'data-formid' ) );
8233 jQuery( trigger ).on( 'click', trashTemplate );
8234 trigger.click();
8235 setTemplateCount( $li.closest( '.accordion-section' ).get( 0 ) );
8236 });
8237
8238 showFreeTemplatesForm = function( $el ) {
8239 var formContainer = document.getElementById( 'frmapi-email-form' );
8240 jQuery.ajax({
8241 dataType: 'json',
8242 url: formContainer.getAttribute( 'data-url' ),
8243 success: function( json ) {
8244 var form = json.renderedHtml;
8245 form = form.replace( /<link\b[^>]*(formidableforms.css|action=frmpro_css)[^>]*>/gi, '' );
8246 formContainer.innerHTML = form;
8247 }
8248 });
8249
8250 $modal.attr( 'frm-page', 'email' );
8251 $modal.attr( 'frm-this-form', $el.attr( 'data-key' ) );
8252 $el.append( installFormTrigger );
8253 };
8254
8255 jQuery( document ).on( 'click', 'li.frm-locked-template .frm-hover-icons .frm-unlock-form', function( event ) {
8256 var $li,
8257 activePage;
8258
8259 event.preventDefault();
8260
8261 $li = jQuery( this ).closest( '.frm-locked-template' );
8262
8263 if ( $li.hasClass( 'frm-free-template' ) ) {
8264 showFreeTemplatesForm( $li );
8265 return;
8266 }
8267
8268 if ( $modal.hasClass( 'frm-expired' ) ) {
8269 activePage = 'renew';
8270 } else {
8271 activePage = 'upgrade';
8272 }
8273
8274 $modal.attr( 'frm-page', activePage );
8275 });
8276
8277 jQuery( document ).on( 'click', '#frm_new_form_modal #frm-template-drop', function() {
8278 jQuery( this )
8279 .closest( '.accordion-section-content' ).css( 'overflow', 'visible' )
8280 .closest( '.accordion-section' ).css( 'z-index', 1 );
8281 });
8282
8283 jQuery( document ).on( 'click', '#frm_new_form_modal #frm-template-drop + .frm-dropdown-menu .frm-build-template', function() {
8284 var name = this.getAttribute( 'data-fullname' ),
8285 link = this.getAttribute( 'data-formid' ),
8286 action = 'frm_build_template';
8287 transitionToAddDetails( $modal, name, link, action );
8288 });
8289
8290 handleError = function( inputId, errorId, type, message ) {
8291 var $error = jQuery( errorId );
8292 $error.removeClass( 'frm_hidden' ).attr( 'frm-error', type );
8293
8294 if ( typeof message !== 'undefined' ) {
8295 $error.find( 'span[frm-error="' + type + '"]' ).text( message );
8296 }
8297
8298 jQuery( inputId ).one( 'keyup', function() {
8299 $error.addClass( 'frm_hidden' );
8300 });
8301 };
8302
8303 handleEmailAddressError = function( type ) {
8304 handleError( '#frm_leave_email', '#frm_leave_email_error', type );
8305 };
8306
8307 jQuery( document ).on( 'click', '#frm-add-my-email-address', function( event ) {
8308 var email = document.getElementById( 'frm_leave_email' ).value.trim(),
8309 regex,
8310 $hiddenForm,
8311 $hiddenEmailField;
8312
8313 event.preventDefault();
8314
8315 if ( '' === email ) {
8316 handleEmailAddressError( 'empty' );
8317 return;
8318 }
8319
8320 regex = /^(([^<>()\[\]\\.,;:\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,}))$/i;
8321
8322 if ( regex.test( email ) === false ) {
8323 handleEmailAddressError( 'invalid' );
8324 return;
8325 }
8326
8327 $hiddenForm = jQuery( '#frmapi-email-form' ).find( 'form' );
8328 $hiddenEmailField = $hiddenForm.find( '[type="email"]' ).not( '.frm_verify' );
8329 if ( ! $hiddenEmailField.length ) {
8330 return;
8331 }
8332
8333 $hiddenEmailField.val( email );
8334 jQuery.ajax({
8335 type: 'POST',
8336 url: $hiddenForm.attr( 'action' ),
8337 data: $hiddenForm.serialize() + '&action=frm_forms_preview'
8338 }).done( function( data ) {
8339 var message = jQuery( data ).find( '.frm_message' ).text().trim();
8340 if ( message.indexOf( 'Thanks!' ) >= 0 ) {
8341 $modal.attr( 'frm-page', 'code' );
8342 } else {
8343 handleEmailAddressError( 'invalid' );
8344 }
8345 });
8346 });
8347
8348 handleConfirmEmailAddressError = function( type, message ) {
8349 handleError( '#frm_code_from_email', '#frm_code_from_email_error', type, message );
8350 };
8351
8352 jQuery( document ).on( 'click', '.frm-confirm-email-address', function( event ) {
8353 var code = document.getElementById( 'frm_code_from_email' ).value.trim();
8354
8355 event.preventDefault();
8356
8357 if ( '' === code ) {
8358 handleConfirmEmailAddressError( 'empty' );
8359 return;
8360 }
8361
8362 jQuery.ajax({
8363 type: 'POST',
8364 url: ajaxurl,
8365 dataType: 'json',
8366 data: {
8367 action: 'template_api_signup',
8368 nonce: frmGlobal.nonce,
8369 code: code,
8370 key: $modal.attr( 'frm-this-form' )
8371 },
8372 success: function( response ) {
8373 if ( response.success ) {
8374 if ( isShowFreeTemplatesFormFirst ) {
8375 // Remove free-templates param from URL then reload page.
8376 urlParams.delete( 'free-templates' );
8377 url.search = urlParams.toString();
8378 window.location.href = url.toString();
8379
8380 return;
8381 }
8382
8383 if ( typeof response.data !== 'undefined' && typeof response.data.url !== 'undefined' ) {
8384 installFormTrigger.setAttribute( 'rel', response.data.url );
8385 installFormTrigger.click();
8386 $modal.attr( 'frm-page', 'details' );
8387
8388 const hookName = 'frm_new_form_modal_form';
8389 wp.hooks.doAction( hookName, $modal );
8390
8391 document.getElementById( 'frm_action_type' ).value = 'frm_install_template';
8392
8393 if ( typeof response.data.urlByKey !== 'undefined' ) {
8394 updateTemplateModalFreeUrls( response.data.urlByKey );
8395 }
8396 }
8397 } else {
8398 if ( Array.isArray( response.data ) && response.data.length ) {
8399 handleConfirmEmailAddressError( 'custom', response.data[0].message );
8400 } else {
8401 handleConfirmEmailAddressError( 'wrong-code' );
8402 }
8403
8404 jQuery( '#frm_code_from_email_options' ).removeClass( 'frm_hidden' );
8405 }
8406 }
8407 });
8408 });
8409
8410 jQuery( document ).on( 'click', '#frm-change-email-address', function() {
8411 $modal.attr( 'frm-page', 'email' );
8412 });
8413
8414 jQuery( document ).on( 'click', '#frm-resend-code', function() {
8415 document.getElementById( 'frm_code_from_email' ).value = '';
8416 jQuery( '#frm_code_from_email_options, #frm_code_from_email_error' ).addClass( 'frm_hidden' );
8417 document.getElementById( 'frm-add-my-email-address' ).click();
8418 });
8419
8420 jQuery( document ).on( 'frmAfterSearch', '#frm_new_form_modal #template-search-input', function() {
8421 var categories = $modal.get( 0 ).querySelector( '.frm-categories-list' ).children,
8422 categoryIndex,
8423 category,
8424 searchableTemplates,
8425 count;
8426
8427 for ( categoryIndex in categories ) {
8428 if ( isNaN( categoryIndex ) ) {
8429 continue;
8430 }
8431
8432 category = categories[ categoryIndex ];
8433 if ( ! category.classList.contains( 'accordion-section' ) ) {
8434 continue;
8435 }
8436
8437 searchableTemplates = category.querySelectorAll( '.frm-searchable-template:not(.frm_hidden)' );
8438 count = searchableTemplates.length;
8439 jQuery( category ).toggleClass( 'frm_hidden', this.value !== '' && ! count );
8440 setTemplateCount( category, searchableTemplates );
8441 }
8442 });
8443
8444 jQuery( document ).on( 'click', '#frm_new_form_modal .frm-modal-back, #frm_new_form_modal .frm_modal_footer .frm-modal-cancel, #frm_new_form_modal .frm-back-to-all-templates', function( event ) {
8445 document.getElementById( 'frm-create-title' ).removeAttribute( 'frm-type' );
8446 $modal.attr( 'frm-page', 'create' );
8447 });
8448
8449 jQuery( document ).on( 'click', '.frm-use-this-template', function( event ) {
8450 var $trigger;
8451
8452 event.preventDefault();
8453
8454 $trigger = activeHoverIcons.find( '.frm-create-form' );
8455 if ( $trigger.closest( '.frm-selectable' ).hasClass( 'frm-locked-template' ) ) {
8456 $trigger = activeHoverIcons.find( '.frm-unlock-form' );
8457 }
8458
8459 $trigger.trigger( 'click' );
8460 });
8461
8462 jQuery( document ).on( 'click', '.frm-submit-new-template', function( event ) {
8463 var button;
8464 event.preventDefault();
8465 button = document.getElementById( 'frm-new-template' ).querySelector( 'button' );
8466 if ( null !== button ) {
8467 button.click();
8468 }
8469 });
8470
8471 if ( urlParams.get( 'triggerNewFormModal' ) ) {
8472 triggerNewFormModal();
8473
8474 if ( isShowFreeTemplatesFormFirst ) {
8475 firstLockedTemplate = jQuery( 'li.frm-locked-template.frm-free-template' ).eq( 0 );
8476
8477 if ( firstLockedTemplate.length ) {
8478 showFreeTemplatesForm( firstLockedTemplate );
8479 }
8480 }
8481 }
8482 }
8483
8484 function updateTemplateModalFreeUrls( urlByKey ) {
8485 jQuery( '#frm_new_form_modal' ).find( '.frm-selectable[data-key]' ).each( function() {
8486 var $template = jQuery( this ),
8487 key = $template.attr( 'data-key' );
8488 if ( 'undefined' !== typeof urlByKey[ key ]) {
8489 $template.removeClass( 'frm-locked-template' );
8490 $template.find( 'h3 svg' ).remove(); // remove the lock from the title
8491 $template.attr( 'data-rel', urlByKey[ key ]);
8492 }
8493 });
8494 }
8495
8496 function transitionToAddDetails( $modal, name, link, action ) {
8497 var nameLabel = document.getElementById( 'frm_new_name' ),
8498 descLabel = document.getElementById( 'frm_new_desc' ),
8499 type = [ 'frm_install_template', 'frm_install_form' ].indexOf( action ) >= 0 ? 'form' : 'template',
8500 templateNameInput = document.getElementById( 'frm_template_name' );
8501
8502 templateNameInput.value = name;
8503 document.getElementById( 'frm_link' ).value = link;
8504 document.getElementById( 'frm_action_type' ).value = action;
8505 nameLabel.textContent = nameLabel.getAttribute( 'data-' + type );
8506 if ( descLabel !== null ) {
8507 descLabel.textContent = descLabel.getAttribute( 'data-' + type );
8508 }
8509
8510 document.getElementById( 'frm-create-title' ).setAttribute( 'frm-type', type );
8511
8512 $modal.attr( 'frm-page', 'details' );
8513
8514 const hookName = 'frm_new_form_modal_form';
8515 wp.hooks.doAction( hookName, $modal );
8516
8517 if ( '' === name ) {
8518 templateNameInput.focus();
8519 }
8520 }
8521
8522 function getStrippedTemplateName( $li ) {
8523 var $clone = $li.find( 'h3' ).clone();
8524 $clone.find( 'svg, .frm-plan-required-tag, .frm-new-pill' ).remove();
8525 return $clone.html().trim();
8526 }
8527
8528 function setTemplateCount( category, searchableTemplates ) {
8529 var count,
8530 templateIndex,
8531 availableCounter,
8532 availableCount;
8533
8534 if ( typeof searchableTemplates === 'undefined' ) {
8535 searchableTemplates = category.querySelectorAll( '.frm-searchable-template:not(.frm_hidden):not(.frm-deleting)' );
8536 }
8537
8538 count = searchableTemplates.length;
8539 category.querySelector( '.frm-template-count' ).textContent = count;
8540
8541 jQuery( category ).find( '.frm-templates-plural' ).toggleClass( 'frm_hidden', count === 1 );
8542 jQuery( category ).find( '.frm-templates-singular' ).toggleClass( 'frm_hidden', count !== 1 );
8543
8544 availableCounter = category.querySelector( '.frm-available-templates-count' );
8545 if ( availableCounter !== null ) {
8546 availableCount = 0;
8547 for ( templateIndex in searchableTemplates ) {
8548 if ( ! isNaN( templateIndex ) && ! searchableTemplates[ templateIndex ].classList.contains( 'frm-locked-template' ) ) {
8549 availableCount++;
8550 }
8551 }
8552
8553 availableCounter.textContent = availableCount;
8554 }
8555 }
8556
8557 function initSelectionAutocomplete() {
8558 frmDom.autocomplete.initSelectionAutocomplete();
8559 }
8560
8561 function nextInstallStep( thisStep ) {
8562 thisStep.classList.add( 'frm_grey' );
8563 thisStep.nextElementSibling.classList.remove( 'frm_grey' );
8564 }
8565
8566 function frmApiPreview( cont, link ) {
8567 cont.innerHTML = '<div class="frm-wait"></div>';
8568 jQuery.ajax({
8569 dataType: 'json',
8570 url: link,
8571 success: function( json ) {
8572 var form = json.renderedHtml;
8573 form = form.replace( /<script\b[^<]*(js\/jquery\/jquery)[^<]*><\/script>/gi, '' );
8574 form = form.replace( /<link\b[^>]*(jquery-ui.min.css)[^>]*>/gi, '' );
8575 form = form.replace( ' frm_logic_form ', ' ' );
8576 form = form.replace( '<form ', '<form onsubmit="event.preventDefault();" ' );
8577 cont.innerHTML = '<div class="frm-wait" id="frm-remove-me"></div><div class="frm-fade" id="frm-show-me">' +
8578 form + '</div>';
8579 setTimeout( function() {
8580 document.getElementById( 'frm-remove-me' ).style.display = 'none';
8581 document.getElementById( 'frm-show-me' ).style.opacity = '1';
8582 }, 300 );
8583 }
8584 });
8585 }
8586
8587 function installTemplateFieldset( e ) {
8588 /*jshint validthis:true */
8589 var fieldset = this.parentNode.parentNode,
8590 action = fieldset.elements.type.value,
8591 button = this;
8592 e.preventDefault();
8593 button.classList.add( 'frm_loading_button' );
8594 installNewForm( fieldset, action, button );
8595 }
8596
8597 function installTemplate( e ) {
8598 /*jshint validthis:true */
8599 var action = this.elements.type.value,
8600 button = this.querySelector( 'button' );
8601 e.preventDefault();
8602 button.classList.add( 'frm_loading_button' );
8603 installNewForm( this, action, button );
8604 }
8605
8606 function installNewForm( form, action, button ) {
8607 const formData = formToData( form );
8608 const formName = formData.template_name;
8609 const formDesc = formData.template_desc;
8610 const link = form.elements.link.value;
8611
8612 let data = {
8613 action: action,
8614 xml: link,
8615 name: formName,
8616 desc: formDesc,
8617 form: JSON.stringify( formData ),
8618 nonce: frmGlobal.nonce
8619 };
8620
8621 const hookName = 'frm_before_install_new_form';
8622 const filterArgs = { formData };
8623 data = wp.hooks.applyFilters( hookName, data, filterArgs );
8624
8625 postAjax( data, function( response ) {
8626 if ( typeof response.redirect !== 'undefined' ) {
8627 const redirect = response.redirect;
8628 if ( typeof form.elements.redirect === 'undefined' ) {
8629 window.location = redirect;
8630 } else {
8631 const href = document.getElementById( 'frm-redirect-link' );
8632 if ( typeof link !== 'undefined' && href !== null ) {
8633 // Show the next installation step.
8634 href.setAttribute( 'href', redirect );
8635 href.classList.remove( 'frm_grey', 'disabled' );
8636 nextInstallStep( form.parentNode.parentNode );
8637 button.classList.add( 'frm_grey', 'disabled' );
8638 }
8639 }
8640 } else {
8641 jQuery( '.spinner' ).css( 'visibility', 'hidden' );
8642
8643 // Show response.message
8644 if ( response.message && typeof form.elements.show_response !== 'undefined' ) {
8645 const showError = document.getElementById( form.elements.show_response.value );
8646 if ( showError !== null ) {
8647 showError.innerHTML = response.message;
8648 showError.classList.remove( 'frm_hidden' );
8649 }
8650 }
8651 }
8652 button.classList.remove( 'frm_loading_button' );
8653 });
8654 }
8655
8656 function handleCaptchaTypeChange( e ) {
8657 const thresholdContainer = document.getElementById( 'frm_captcha_threshold_container' );
8658 if ( thresholdContainer ) {
8659 thresholdContainer.classList.toggle( 'frm_hidden', 'v3' !== e.target.value );
8660 }
8661 }
8662
8663 function trashTemplate( e ) {
8664 /*jshint validthis:true */
8665 var id = this.getAttribute( 'data-id' );
8666 e.preventDefault();
8667
8668 data = {
8669 action: 'frm_forms_trash',
8670 id: id,
8671 nonce: frmGlobal.nonce
8672 };
8673 postAjax( data, function() {
8674 var card = document.getElementById( 'frm-template-custom-' + id );
8675 fadeOut( card, function() {
8676 card.parentNode.removeChild( card );
8677 });
8678 });
8679 }
8680
8681 function searchContent() {
8682 /*jshint validthis:true */
8683 var i,
8684 regEx = false,
8685 searchText = this.value.toLowerCase(),
8686 toSearch = this.getAttribute( 'data-tosearch' ),
8687 items = document.getElementsByClassName( toSearch );
8688
8689 if ( this.tagName === 'SELECT' ) {
8690 searchText = selectedOptions( this );
8691 searchText = searchText.join( '|' ).toLowerCase();
8692 regEx = true;
8693 }
8694
8695 if ( toSearch === 'frm-action' && searchText !== '' ) {
8696 var addons = document.getElementById( 'frm_email_addon_menu' ).classList;
8697 addons.remove( 'frm-all-actions' );
8698 addons.add( 'frm-limited-actions' );
8699 }
8700
8701 for ( i = 0; i < items.length; i++ ) {
8702 var innerText = items[i].innerText.toLowerCase();
8703 if ( searchText === '' ) {
8704 items[i].classList.remove( 'frm_hidden' );
8705 items[i].classList.remove( 'frm-search-result' );
8706 } else if ( ( regEx && new RegExp( searchText ).test( innerText ) ) || innerText.indexOf( searchText ) >= 0 ) {
8707 items[i].classList.remove( 'frm_hidden' );
8708 items[i].classList.add( 'frm-search-result' );
8709 } else {
8710 items[i].classList.add( 'frm_hidden' );
8711 items[i].classList.remove( 'frm-search-result' );
8712 }
8713 }
8714
8715 jQuery( this ).trigger( 'frmAfterSearch' );
8716 }
8717
8718 function stopPropagation( e ) {
8719 e.stopPropagation();
8720 }
8721
8722 /* Helpers */
8723
8724 function selectedOptions( select ) {
8725 var opt,
8726 result = [],
8727 options = select && select.options;
8728
8729 for ( var i = 0, iLen = options.length; i < iLen; i++ ) {
8730 opt = options[i];
8731
8732 if ( opt.selected ) {
8733 result.push( opt.value );
8734 }
8735 }
8736 return result;
8737 }
8738
8739 function triggerEvent( element, event ) {
8740 var evt = document.createEvent( 'HTMLEvents' );
8741 evt.initEvent( event, false, true );
8742 element.dispatchEvent( evt );
8743 }
8744
8745 function postAjax( data, success ) {
8746 let response;
8747
8748 const xmlHttp = new XMLHttpRequest();
8749 const params = typeof data === 'string' ? data : Object.keys( data ).map(
8750 function( k ) {
8751 return encodeURIComponent( k ) + '=' + encodeURIComponent( data[k]);
8752 }
8753 ).join( '&' );
8754
8755 xmlHttp.open( 'post', ajaxurl, true );
8756 xmlHttp.onreadystatechange = function() {
8757 if ( xmlHttp.readyState > 3 && xmlHttp.status == 200 ) {
8758 response = xmlHttp.responseText;
8759 try {
8760 response = JSON.parse( response );
8761 } catch ( e ) {
8762 // The response may not be JSON, so just return it.
8763 }
8764 success( response );
8765 }
8766 };
8767 xmlHttp.setRequestHeader( 'X-Requested-With', 'XMLHttpRequest' );
8768 xmlHttp.setRequestHeader( 'Content-type', 'application/x-www-form-urlencoded' );
8769 xmlHttp.send( params );
8770 return xmlHttp;
8771 }
8772
8773 function fadeOut( element, success ) {
8774 element.classList.add( 'frm-fade' );
8775 setTimeout( success, 1000 );
8776 }
8777
8778 function invisible( classes ) {
8779 jQuery( classes ).css( 'visibility', 'hidden' );
8780 }
8781
8782 function visible( classes ) {
8783 jQuery( classes ).css( 'visibility', 'visible' );
8784 }
8785
8786 function initModal( id, width ) {
8787 const $info = jQuery( id );
8788 if ( ! $info.length ) {
8789 return false;
8790 }
8791
8792 if ( typeof width === 'undefined' ) {
8793 width = '550px';
8794 }
8795
8796 const dialogArgs = {
8797 dialogClass: 'frm-dialog',
8798 modal: true,
8799 autoOpen: false,
8800 closeOnEscape: true,
8801 width: width,
8802 resizable: false,
8803 draggable: false,
8804 open: function() {
8805 jQuery( '.ui-dialog-titlebar' ).addClass( 'frm_hidden' ).removeClass( 'ui-helper-clearfix' );
8806 jQuery( '#wpwrap' ).addClass( 'frm_overlay' );
8807 jQuery( '.frm-dialog' ).removeClass( 'ui-widget ui-widget-content ui-corner-all' );
8808 $info.removeClass( 'ui-dialog-content ui-widget-content' );
8809 bindClickForDialogClose( $info );
8810 },
8811 close: function() {
8812 jQuery( '#wpwrap' ).removeClass( 'frm_overlay' );
8813 jQuery( '.spinner' ).css( 'visibility', 'hidden' );
8814
8815 this.removeAttribute( 'data-option-type' );
8816 const optionType = document.getElementById( 'bulk-option-type' );
8817 if ( optionType ) {
8818 optionType.value = '';
8819 }
8820 }
8821 };
8822
8823 $info.dialog( dialogArgs );
8824
8825 return $info;
8826 }
8827
8828 function toggle( cname, id ) {
8829 if ( id === '#' ) {
8830 var cont = document.getElementById( cname );
8831 var hidden = cont.style.display;
8832 if ( hidden === 'none' ) {
8833 cont.style.display = 'block';
8834 } else {
8835 cont.style.display = 'none';
8836 }
8837 } else {
8838 var vis = cname.is( ':visible' );
8839 if ( vis ) {
8840 cname.hide();
8841 } else {
8842 cname.show();
8843 }
8844 }
8845 }
8846
8847 function removeWPUnload() {
8848 window.onbeforeunload = null;
8849 var w = jQuery( window );
8850 w.off( 'beforeunload.widgets' );
8851 w.off( 'beforeunload.edit-post' );
8852 }
8853
8854 function addMultiselectLabelListener() {
8855 const clickListener = ( e ) => {
8856 if ( 'LABEL' !== e.target.nodeName ) {
8857 return;
8858 }
8859
8860 const labelFor = e.target.getAttribute( 'for' );
8861 if ( ! labelFor ) {
8862 return;
8863 }
8864
8865 const input = document.getElementById( labelFor );
8866 if ( ! input || ! input.nextElementSibling ) {
8867 return;
8868 }
8869
8870 const buttonToggle = input.nextElementSibling.querySelector( 'button.dropdown-toggle.multiselect' );
8871 if ( ! buttonToggle ) {
8872 return;
8873 }
8874
8875 const triggerMultiselectClick = () => buttonToggle.click();
8876 setTimeout( triggerMultiselectClick, 0 );
8877 };
8878 document.addEventListener( 'click', clickListener );
8879 }
8880
8881 function maybeChangeEmbedFormMsg() {
8882 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
8883 var fieldItem = document.getElementById( 'frm_field_id_' + fieldId );
8884 if ( null === fieldItem || 'form' !== fieldItem.dataset.type ) {
8885 return;
8886 }
8887
8888 fieldItem = jQuery( fieldItem );
8889
8890 if ( this.options[ this.selectedIndex ].value ) {
8891 fieldItem.find( '.frm-not-set' )[0].classList.add( 'frm_hidden' );
8892 var embedMsg = fieldItem.find( '.frm-embed-message' );
8893 embedMsg.html( embedMsg.data( 'embedmsg' ) + this.options[ this.selectedIndex ].text );
8894 fieldItem.find( '.frm-embed-field-placeholder' )[0].classList.remove( 'frm_hidden' );
8895 } else {
8896 fieldItem.find( '.frm-not-set' )[0].classList.remove( 'frm_hidden' );
8897 fieldItem.find( '.frm-embed-field-placeholder' )[0].classList.add( 'frm_hidden' );
8898 }
8899 }
8900
8901 function toggleProductType() {
8902 var settings = jQuery( this ).closest( '.frm-single-settings' ),
8903 container = settings.find( '.frmjs_product_choices' ),
8904 heading = settings.find( '.frm_prod_options_heading' ),
8905 currentVal = this.options[ this.selectedIndex ].value;
8906
8907 container.removeClass( 'frm_prod_type_single frm_prod_type_user_def' );
8908 heading.removeClass( 'frm_prod_user_def' );
8909
8910 if ( 'single' === currentVal ) {
8911 container.addClass( 'frm_prod_type_single' );
8912 } else if ( 'user_def' === currentVal ) {
8913 container.addClass( 'frm_prod_type_user_def' );
8914 heading.addClass( 'frm_prod_user_def' );
8915 }
8916 }
8917
8918 function isProductField( fieldId ) {
8919 var field = document.getElementById( 'frm_field_id_' + fieldId );
8920 if ( field === null ) {
8921 return false;
8922 } else {
8923 return 'product' === field.getAttribute( 'data-type' );
8924 }
8925 }
8926
8927 /**
8928 * Serialize form data with vanilla JS.
8929 */
8930 function formToData( form ) {
8931 var subKey, i,
8932 object = {},
8933 formData = form.elements;
8934
8935 for ( i = 0; i < formData.length; i++ ) {
8936 var input = formData[i],
8937 key = input.name,
8938 value = input.value,
8939 names = key.match( /(.*)\[(.*)\]/ );
8940
8941 if ( ( input.type === 'radio' || input.type === 'checkbox' ) && ! input.checked ) {
8942 continue;
8943 }
8944
8945 if ( names !== null ) {
8946 key = names[1];
8947 subKey = names[2];
8948 if ( ! Reflect.has( object, key ) ) {
8949 object[key] = {};
8950 }
8951 object[key][subKey] = value;
8952 continue;
8953 }
8954
8955 // Reflect.has in favor of: object.hasOwnProperty(key)
8956 if ( ! Reflect.has( object, key ) ) {
8957 object[key] = value;
8958 continue;
8959 }
8960 if ( ! Array.isArray( object[key]) ) {
8961 object[key] = [ object[key] ];
8962 }
8963 object[key].push( value );
8964 }
8965
8966 return object;
8967 }
8968
8969 /**
8970 * Show, hide, and sort subfields of Name field on form builder.
8971 *
8972 * @since 4.11
8973 */
8974 function handleNameFieldOnFormBuilder() {
8975 /**
8976 * Gets subfield element from cache.
8977 *
8978 * @param {String} fieldId Field ID.
8979 * @param {String} key Cache key.
8980 * @returns {HTMLElement|undefined} Return the element from cache or undefined if not found.
8981 */
8982 const getSubFieldElFromCache = ( fieldId, key ) => {
8983 window.frmCachedSubFields = window.frmCachedSubFields || {};
8984 window.frmCachedSubFields[fieldId] = window.frmCachedSubFields[fieldId] || {};
8985 return window.frmCachedSubFields[fieldId][key];
8986 };
8987
8988 /**
8989 * Sets subfield element to cache.
8990 *
8991 * @param {String} fieldId Field ID.
8992 * @param {String} key Cache key.
8993 * @param {HTMLElement} el Element.
8994 */
8995 const setSubFieldElToCache = ( fieldId, key, el ) => {
8996 window.frmCachedSubFields = window.frmCachedSubFields || {};
8997 window.frmCachedSubFields[fieldId] = window.frmCachedSubFields[fieldId] || {};
8998 window.frmCachedSubFields[fieldId][key] = el;
8999 };
9000
9001 /**
9002 * Gets column class from the number of columns.
9003 *
9004 * @param {Number} colCount Number of columns.
9005 * @returns {string}
9006 */
9007 const getColClass = colCount => 'frm' + parseInt( 12 / colCount );
9008
9009 const colClasses = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ].map( num => 'frm' + num );
9010
9011 const allSubFieldNames = [ 'first', 'middle', 'last' ];
9012
9013 /**
9014 * Handles name layout change.
9015 *
9016 * @param {Event} event Event object.
9017 */
9018 const onChangeLayout = event => {
9019 const value = event.target.value;
9020 const subFieldNames = value.split( '_' );
9021 const fieldId = event.target.dataset.fieldId;
9022
9023 /*
9024 * Live update form on the form builder.
9025 */
9026 const container = document.querySelector( '#field_' + fieldId + '_inner_container .frm_combo_inputs_container' );
9027 const newColClass = getColClass( subFieldNames.length );
9028
9029 // Set all sub field elements to cache and hide all of them first.
9030 allSubFieldNames.forEach( name => {
9031 const subFieldEl = container.querySelector( '[data-sub-field-name="' + name + '"]' );
9032 if ( subFieldEl ) {
9033 subFieldEl.classList.add( 'frm_hidden' );
9034 subFieldEl.classList.remove( ...colClasses );
9035 setSubFieldElToCache( fieldId, name, subFieldEl );
9036 }
9037 });
9038
9039 subFieldNames.forEach( subFieldName => {
9040 const subFieldEl = getSubFieldElFromCache( fieldId, subFieldName );
9041 if ( ! subFieldEl ) {
9042 return;
9043 }
9044
9045 subFieldEl.classList.remove( 'frm_hidden' );
9046 subFieldEl.classList.add( newColClass );
9047
9048 container.append( subFieldEl );
9049 });
9050
9051 /*
9052 * Live update subfield options.
9053 */
9054 // Hide all subfield options.
9055 allSubFieldNames.forEach( name => {
9056 const optionsEl = document.querySelector( '.frm_sub_field_options-' + name + '[data-field-id="' + fieldId + '"]' );
9057 if ( optionsEl ) {
9058 optionsEl.classList.add( 'frm_hidden' );
9059 setSubFieldElToCache( fieldId, name + '_options', optionsEl );
9060 }
9061 });
9062
9063 subFieldNames.forEach( subFieldName => {
9064 const optionsEl = getSubFieldElFromCache( fieldId, subFieldName + '_options' );
9065 if ( ! optionsEl ) {
9066 return;
9067 }
9068 optionsEl.classList.remove( 'frm_hidden' );
9069 });
9070 };
9071
9072 const dropdownSelector = '.frm_name_layout_dropdown';
9073 document.addEventListener( 'change', event => {
9074 if ( event.target.matches( dropdownSelector ) ) {
9075 onChangeLayout( event );
9076 }
9077 }, false );
9078 }
9079
9080 function debounce( func, wait = 100 ) {
9081 return frmDom.util.debounce( func, wait );
9082 }
9083
9084 function addSaveAndDragIconsToOption( fieldId, liObject ) {
9085 let li, useTag, useTagHref;
9086 let hasDragIcon = false;
9087 let hasSaveIcon = false;
9088
9089 if ( liObject.newOption ) {
9090 const parser = new DOMParser();
9091 li = parser.parseFromString( liObject.newOption, 'text/html' ).body.childNodes[0];
9092 } else {
9093 li = liObject;
9094 }
9095
9096 const liIcons = li.querySelectorAll( 'svg' );
9097
9098 liIcons.forEach( ( svg, key ) => {
9099 useTag = svg.getElementsByTagNameNS( 'http://www.w3.org/2000/svg', 'use' )[0];
9100 if ( ! useTag ) {
9101 return;
9102 }
9103 useTagHref = useTag.getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' ) || useTag.getAttribute( 'href' );
9104
9105 if ( useTagHref === '#frm_drag_icon' ) {
9106 hasDragIcon = true;
9107 }
9108
9109 if ( useTagHref === '#frm_save_icon' ) {
9110 hasSaveIcon = true;
9111 }
9112 });
9113
9114 if ( ! hasDragIcon ) {
9115 li.prepend( icons.drag.cloneNode( true ) );
9116 }
9117
9118 if ( li.querySelector( `[id^=field_key_${fieldId}-]` ) && ! hasSaveIcon ) {
9119 li.querySelector( `[id^=field_key_${fieldId}-]` ).after( icons.save.cloneNode( true ) );
9120 }
9121
9122 if ( liObject.newOption ) {
9123 liObject.newOption = li;
9124 }
9125 }
9126
9127 function maybeAddSaveAndDragIcons( fieldId ) {
9128 fieldOptions = document.querySelectorAll( `[id^=frm_delete_field_${fieldId}-]` );
9129 // return if there are no options.
9130 if ( fieldOptions.length < 2 ) {
9131 return;
9132 }
9133
9134 let options = [ ...fieldOptions ].slice( 1 );
9135 options.forEach( ( li, _key ) => {
9136 if ( li.classList.contains( 'frm_other_option' ) ) {
9137 return;
9138 }
9139 addSaveAndDragIconsToOption( fieldId, li );
9140 });
9141 }
9142
9143 /**
9144 * Does the same as jQuery( document ).on( 'event', 'selector', handler ).
9145 *
9146 * @since 5.4.2
9147 *
9148 * @param {String} event Event name.
9149 * @param {String} selector Selector.
9150 * @param {Function} handler Handler.
9151 * @param {Boolean|Object} options Options to be added to `addEventListener()` method. Default is `false`.
9152 */
9153 function documentOn( event, selector, handler, options ) {
9154 if ( 'undefined' === typeof options ) {
9155 options = false;
9156 }
9157
9158 document.addEventListener( event, function( e ) {
9159 var target;
9160
9161 // loop parent nodes from the target to the delegation node.
9162 for ( target = e.target; target && target != this; target = target.parentNode ) {
9163 if ( target.matches( selector ) ) {
9164 handler.call( target, e );
9165 break;
9166 }
9167 }
9168 }, options );
9169 }
9170
9171 return {
9172 init: function() {
9173 s = {};
9174
9175 // Bootstrap dropdown button
9176 jQuery( '.wp-admin' ).on( 'click', function( e ) {
9177 var t = jQuery( e.target );
9178 var $openDrop = jQuery( '.dropdown.open' );
9179 if ( $openDrop.length && ! t.hasClass( 'dropdown' ) && ! t.closest( '.dropdown' ).length ) {
9180 $openDrop.removeClass( 'open' );
9181 }
9182 });
9183 jQuery( '#frm_bs_dropdown:not(.open) a' ).on( 'click', focusSearchBox );
9184
9185 if ( typeof thisFormId === 'undefined' ) {
9186 thisFormId = jQuery( document.getElementById( 'form_id' ) ).val();
9187 }
9188
9189 frmAdminBuild.inboxBannerInit();
9190
9191 if ( $newFields.length > 0 ) {
9192 // only load this on the form builder page
9193 frmAdminBuild.buildInit();
9194 } else if ( document.getElementById( 'frm_notification_settings' ) !== null ) {
9195 // only load on form settings page
9196 frmAdminBuild.settingsInit();
9197 } else if ( document.getElementById( 'frm_styling_form' ) !== null ) {
9198 // load styling settings js
9199 frmAdminBuild.styleInit();
9200 } else if ( document.getElementById( 'form_global_settings' ) !== null ) {
9201 // global settings page
9202 frmAdminBuild.globalSettingsInit();
9203 } else if ( document.getElementById( 'frm_export_xml' ) !== null ) {
9204 // import/export page
9205 frmAdminBuild.exportInit();
9206 } else if ( document.getElementById( 'frm_dyncontent' ) !== null ) {
9207 // only load on views settings page
9208 frmAdminBuild.viewInit();
9209 } else if ( document.getElementById( 'frm_inbox_page' ) !== null ) {
9210 // Inbox page
9211 frmAdminBuild.inboxInit();
9212 } else if ( document.getElementById( 'frm-welcome' ) !== null ) {
9213 // Solution install page
9214 frmAdminBuild.solutionInit();
9215 } else {
9216 // New form selection page
9217 initNewFormModal();
9218 initSelectionAutocomplete();
9219
9220 jQuery( '[data-frmprint]' ).on( 'click', function() {
9221 window.print();
9222 return false;
9223 });
9224 }
9225
9226 var $advInfo = jQuery( document.getElementById( 'frm_adv_info' ) );
9227 if ( $advInfo.length > 0 || jQuery( '.frm_field_list' ).length > 0 ) {
9228 // only load on the form, form settings, and view settings pages
9229 frmAdminBuild.panelInit();
9230 }
9231
9232 loadTooltips();
9233 initUpgradeModal();
9234 moveTopBanner();
9235
9236 // used on build, form settings, and view settings
9237 var $shortCodeDiv = jQuery( document.getElementById( 'frm_shortcodediv' ) );
9238 if ( $shortCodeDiv.length > 0 ) {
9239 jQuery( 'a.edit-frm_shortcode' ).on( 'click', function() {
9240 if ( $shortCodeDiv.is( ':hidden' ) ) {
9241 $shortCodeDiv.slideDown( 'fast' );
9242 this.style.display = 'none';
9243 }
9244 return false;
9245 });
9246
9247 jQuery( '.cancel-frm_shortcode', '#frm_shortcodediv' ).on( 'click', function() {
9248 $shortCodeDiv.slideUp( 'fast' );
9249 $shortCodeDiv.siblings( 'a.edit-frm_shortcode' ).show();
9250 return false;
9251 });
9252 }
9253
9254 // tabs
9255 jQuery( document ).on( 'click', '#frm-nav-tabs a', clickNewTab );
9256 jQuery( '.post-type-frm_display .frm-nav-tabs a, .frm-category-tabs a' ).on( 'click', function() {
9257 const showUpgradeTab = this.classList.contains( 'frm_show_upgrade_tab' );
9258 if ( this.classList.contains( 'frm_noallow' ) && ! showUpgradeTab ) {
9259 return;
9260 }
9261
9262 if ( showUpgradeTab ) {
9263 populateUpgradeTab( this );
9264 }
9265
9266 clickTab( this );
9267 return false;
9268 });
9269 clickTab( jQuery( '.starttab a' ), 'auto' );
9270
9271 // submit the search form with dropdown
9272 jQuery( document ).on( 'click', '#frm-fid-search-menu a', function() {
9273 var val = this.id.replace( 'fid-', '' );
9274 jQuery( 'select[name="fid"]' ).val( val );
9275 triggerSubmit( document.getElementById( 'posts-filter' ) );
9276 return false;
9277 });
9278
9279 jQuery( '.frm_select_box' ).on( 'click focus', function() {
9280 this.select();
9281 });
9282
9283 jQuery( document ).on( 'input search change', '.frm-auto-search', searchContent );
9284 jQuery( document ).on( 'focusin click', '.frm-auto-search', stopPropagation );
9285 var autoSearch = jQuery( '.frm-auto-search' );
9286 if ( autoSearch.val() !== '' ) {
9287 autoSearch.trigger( 'keyup' );
9288 }
9289
9290 // Initialize Formidable Connection.
9291 FrmFormsConnect.init();
9292
9293 jQuery( document ).on( 'click', '.frm-install-addon', installAddon );
9294 jQuery( document ).on( 'click', '.frm-activate-addon', activateAddon );
9295 jQuery( document ).on( 'click', '.frm-solution-multiple', installMultipleAddons );
9296
9297 // prevent annoying confirmation message from WordPress
9298 jQuery( 'button, input[type=submit]' ).on( 'click', removeWPUnload );
9299
9300 addMultiselectLabelListener();
9301
9302 frmAdminBuild.hooks.addFilter(
9303 'frm_before_embed_modal',
9304 ( ids, { element, type }) => {
9305 if ( 'form' !== type ) {
9306 return ids;
9307 }
9308
9309 let formId, formKey;
9310 const row = element.closest( 'tr' );
9311
9312 if ( row ) {
9313 // Embed icon on form index.
9314 formId = parseInt( row.querySelector( '.column-id' ).textContent );
9315 formKey = row.querySelector( '.column-form_key' ).textContent;
9316 } else {
9317 // Embed button in form builder / form settings.
9318 formId = document.getElementById( 'form_id' ).value;
9319
9320 const formKeyInput = document.getElementById( 'frm_form_key' );
9321 if ( formKeyInput ) {
9322 formKey = formKeyInput.value;
9323 } else {
9324 const previewDrop = document.getElementById( 'frm-previewDrop' );
9325 if ( previewDrop ) {
9326 formKey = previewDrop.nextElementSibling.querySelector( '.dropdown-item a' ).getAttribute( 'href' ).split( 'form=' )[1];
9327 }
9328 }
9329 }
9330
9331 return [ formId, formKey ];
9332 }
9333 );
9334
9335 document.querySelectorAll( '#frm-show-fields > li, .frm_grid_container li' ).forEach( ( el, _key ) => {
9336 el.addEventListener( 'click', function() {
9337 let fieldId = this.querySelector( 'li' )?.dataset.fid || this.dataset.fid;
9338 maybeAddSaveAndDragIcons( fieldId );
9339 });
9340 });
9341 },
9342
9343 buildInit: function() {
9344 var loadFieldId, $builderForm, builderArea;
9345
9346 if ( jQuery( '.frm_field_loading' ).length ) {
9347 loadFieldId = jQuery( '.frm_field_loading' ).first().attr( 'id' );
9348 loadFields( loadFieldId );
9349 }
9350
9351 setupSortable( 'ul.frm_sorting' );
9352
9353 jQuery( '.field_type_list > li:not(.frm_noallow)' ).draggable({
9354 connectToSortable: '#frm-show-fields',
9355 helper: 'clone',
9356 revert: 'invalid',
9357 delay: 10,
9358 cancel: '.frm-dropdown-menu'
9359 });
9360 jQuery( 'ul.field_type_list, .field_type_list li, ul.frm_code_list, .frm_code_list li, .frm_code_list li a, #frm_adv_info #category-tabs li, #frm_adv_info #category-tabs li a' ).disableSelection();
9361
9362 jQuery( '.frm_submit_ajax' ).on( 'click', submitBuild );
9363 jQuery( '.frm_submit_no_ajax' ).on( 'click', submitNoAjax );
9364
9365 jQuery( 'a.edit-form-status' ).on( 'click', slideDown );
9366 jQuery( '.cancel-form-status' ).on( 'click', slideUp );
9367 jQuery( '.save-form-status' ).on( 'click', function() {
9368 var newStatus = jQuery( document.getElementById( 'form_change_status' ) ).val();
9369 jQuery( 'input[name="new_status"]' ).val( newStatus );
9370 jQuery( document.getElementById( 'form-status-display' ) ).html( newStatus );
9371 jQuery( '.cancel-form-status' ).trigger( 'click' );
9372 return false;
9373 });
9374
9375 jQuery( '.frm_form_builder form' ).first().on( 'submit', function() {
9376 jQuery( '.inplace_field' ).trigger( 'blur' );
9377 });
9378
9379 initiateMultiselect();
9380 renumberPageBreaks();
9381
9382 $builderForm = jQuery( builderForm );
9383 builderArea = document.getElementById( 'frm_form_editor_container' );
9384 $builderForm.on( 'click', '.frm_add_logic_row', addFieldLogicRow );
9385 $builderForm.on( 'click', '.frm_add_watch_lookup_row', addWatchLookupRow );
9386 $builderForm.on( 'change', '.frm_get_values_form', updateGetValueFieldSelection );
9387 $builderForm.on( 'change', '.frm_logic_field_opts', getFieldValues );
9388 $builderForm.on( 'change', '.scale_maxnum, .scale_minnum', setScaleValues );
9389 $builderForm.on( 'change', '.radio_maxnum', setStarValues );
9390 $builderForm.on( 'frm-multiselect-changed', 'select[name^="field_options[admin_only_"]', adjustVisibilityValuesForEveryoneValues );
9391
9392 jQuery( document.getElementById( 'frm-insert-fields' ) ).on( 'click', '.frm_add_field', addFieldClick );
9393 $newFields.on( 'click', '.frm_clone_field', duplicateField );
9394 $builderForm.on( 'blur', 'input[id^="frm_calc"]', checkCalculationCreatedByUser );
9395 $builderForm.on( 'change', 'input.frm_format_opt, input.frm_max_length_opt', toggleInvalidMsg );
9396 $builderForm.on( 'change click', '[data-changeme]', liveChanges );
9397 $builderForm.on( 'click', 'input.frm_req_field', markRequired );
9398 $builderForm.on( 'click', '.frm_mark_unique', markUnique );
9399
9400 $builderForm.on( 'change', '.frm_repeat_format', toggleRepeatButtons );
9401 $builderForm.on( 'change', '.frm_repeat_limit', checkRepeatLimit );
9402 $builderForm.on( 'change', '.frm_js_checkbox_limit', checkCheckboxSelectionsLimit );
9403 $builderForm.on( 'input', 'input[name^="field_options[add_label_"]', function() {
9404 updateRepeatText( this, 'add' );
9405 });
9406 $builderForm.on( 'input', 'input[name^="field_options[remove_label_"]', function() {
9407 updateRepeatText( this, 'remove' );
9408 });
9409 $builderForm.on( 'change', 'select[name^="field_options[data_type_"]', maybeClearWatchFields );
9410 jQuery( builderArea ).on( 'click', '.frm-collapse-page', maybeCollapsePage );
9411 jQuery( builderArea ).on( 'click', '.frm-collapse-section', maybeCollapseSection );
9412 $builderForm.on( 'click', '.frm-single-settings h3', maybeCollapseSettings );
9413
9414 $builderForm.on( 'click', '.frm_toggle_sep_values', toggleSepValues );
9415 $builderForm.on( 'click', '.frm_toggle_image_options', toggleImageOptions );
9416 $builderForm.on( 'click', '.frm_remove_image_option', removeImageFromOption );
9417 $builderForm.on( 'click', '.frm_choose_image_box', addImageToOption );
9418 $builderForm.on( 'change', '.frm_hide_image_text', refreshOptionDisplay );
9419 $builderForm.on( 'change', '.frm_field_options_image_size', setImageSize );
9420 $builderForm.on( 'click', '.frm_multiselect_opt', toggleMultiselect );
9421 $newFields.on( 'mousedown', 'input, textarea, select', stopFieldFocus );
9422 $newFields.on( 'click', 'input[type=radio], input[type=checkbox]', stopFieldFocus );
9423 $newFields.on( 'click', '.frm_delete_field', clickDeleteField );
9424 $newFields.on( 'click', '.frm_select_field', clickSelectField );
9425 jQuery( document ).on( 'click', '.frm_delete_field_group', clickDeleteFieldGroup );
9426 jQuery( document ).on( 'click', '.frm_clone_field_group', duplicateFieldGroup );
9427 jQuery( document ).on( 'click', '#frm_field_group_controls > span:first-child', clickFieldGroupLayout );
9428 jQuery( document ).on( 'click', '.frm-row-layout-option', handleFieldGroupLayoutOptionClick );
9429 jQuery( document ).on( 'click', '.frm-merge-fields-into-row .frm-row-layout-option', handleFieldGroupLayoutOptionInsideMergeClick );
9430 jQuery( document ).on( 'click', '.frm-custom-field-group-layout', customFieldGroupLayoutClick );
9431 jQuery( document ).on( 'click', '.frm-merge-fields-into-row .frm-custom-field-group-layout', customFieldGroupLayoutInsideMergeClick );
9432 jQuery( document ).on( 'click', '.frm-break-field-group', breakFieldGroupClick );
9433 $newFields.on( 'click', '#frm_field_group_popup .frm_grid_container input', focusFieldGroupInputOnClick );
9434 jQuery( document ).on( 'click', '.frm-cancel-custom-field-group-layout', cancelCustomFieldGroupClick );
9435 jQuery( document ).on( 'click', '.frm-save-custom-field-group-layout', saveCustomFieldGroupClick );
9436 $newFields.on( 'click', 'ul.frm_sorting', fieldGroupClick );
9437 jQuery( document ).on( 'click', '.frm-merge-fields-into-row', mergeFieldsIntoRowClick );
9438 jQuery( document ).on( 'click', '.frm-delete-field-groups', deleteFieldGroupsClick );
9439 $newFields.on( 'click', '.frm-field-action-icons [data-toggle="dropdown"]', function() {
9440 this.closest( 'li.form-field' ).classList.add( 'frm-field-settings-open' );
9441 jQuery( document ).on( 'click', '#frm_builder_page', handleClickOutsideOfFieldSettings );
9442 });
9443 $newFields.on( 'mousemove', 'ul.frm_sorting', checkForMultiselectKeysOnMouseMove );
9444 $newFields.on( 'show.bs.dropdown', '.frm-field-action-icons', onFieldActionDropdownShow );
9445 jQuery( document ).on( 'show.bs.dropdown', '#frm_field_group_controls', onFieldGroupActionDropdownShow );
9446 $builderForm.on( 'click', '.frm_single_option a[data-removeid]', deleteFieldOption );
9447 $builderForm.on( 'mousedown', '.frm_single_option input[type=radio]', maybeUncheckRadio );
9448 $builderForm.on( 'focusin', '.frm_single_option input[type=text]', maybeClearOptText );
9449 $builderForm.on( 'click', '.frm_add_opt', addFieldOption );
9450 $builderForm.on( 'change', '.frm_single_option input', resetOptOnChange );
9451 $builderForm.on( 'change', '.frm_image_id', resetOptOnChange );
9452 $builderForm.on( 'change', '.frm_toggle_mult_sel', toggleMultSel );
9453 $builderForm.on( 'focusin', '.frm_classes', showBuilderModal );
9454
9455 $newFields.on( 'click', '.frm_primary_label', clickLabel );
9456 $newFields.on( 'click', '.frm_description', clickDescription );
9457 $newFields.on( 'click', 'li.ui-state-default', clickVis );
9458 $newFields.on( 'dblclick', 'li.ui-state-default', openAdvanced );
9459 $builderForm.on( 'change', '.frm_tax_form_select', toggleFormTax );
9460 $builderForm.on( 'change', 'select.conf_field', addConf );
9461
9462 $builderForm.on( 'change', '.frm_get_field_selection', getFieldSelection );
9463
9464 $builderForm.on( 'click', '.frm-show-inline-modal', maybeShowInlineModal );
9465
9466 $builderForm.on( 'click', '.frm-inline-modal .dismiss', dismissInlineModal );
9467 jQuery( document ).on( 'change', '[data-frmchange]', changeInputtedValue );
9468
9469 $builderForm.on( 'change', '.frm_include_extras_field', rePopCalcFieldsForSummary );
9470 $builderForm.on( 'change', 'select[name^="field_options[form_select_"]', maybeChangeEmbedFormMsg );
9471
9472 jQuery( document ).on( 'submit', '#frm_js_build_form', buildSubmittedNoAjax );
9473 jQuery( document ).on( 'change', '#frm_builder_page input:not(.frm-search-input):not(.frm-custom-grid-size-input), #frm_builder_page select, #frm_builder_page textarea', fieldUpdated );
9474
9475 popAllProductFields();
9476
9477 jQuery( document ).on( 'change', '.frmjs_prod_data_type_opt', toggleProductType );
9478
9479 jQuery( document ).on( 'focus', '.frm-single-settings ul input[type="text"][name^="field_options[options_"]', onOptionTextFocus );
9480 jQuery( document ).on( 'blur', '.frm-single-settings ul input[type="text"][name^="field_options[options_"]', onOptionTextBlur );
9481
9482 initBulkOptionsOverlay();
9483 hideEmptyEle();
9484 maybeHideQuantityProductFieldOption();
9485 handleNameFieldOnFormBuilder();
9486 toggleSectionHolder();
9487 },
9488
9489 settingsInit: function() {
9490 const $formActions = jQuery( document.getElementById( 'frm_notification_settings' ) );
9491
9492 let formSettings, $loggedIn, $cookieExp, $editable;
9493
9494 // BCC, CC, and Reply To button functionality
9495 $formActions.on( 'click', '.frm_email_buttons', showEmailRow );
9496 $formActions.on( 'click', '.frm_remove_field', hideEmailRow );
9497 $formActions.on( 'change', '.frm_to_row, .frm_from_row', showEmailWarning );
9498 $formActions.on( 'change', '.frm_tax_selector', changePosttaxRow );
9499 $formActions.on( 'change', 'select.frm_single_post_field', checkDupPost );
9500 $formActions.on( 'change', 'select.frm_toggle_post_content', togglePostContent );
9501 $formActions.on( 'change', 'select.frm_dyncontent_opt', fillDyncontent );
9502 $formActions.on( 'change', '.frm_post_type', switchPostType );
9503 $formActions.on( 'click', '.frm_add_postmeta_row', addPostmetaRow );
9504 $formActions.on( 'click', '.frm_add_posttax_row', addPosttaxRow );
9505 $formActions.on( 'click', '.frm_toggle_cf_opts', toggleCfOpts );
9506 $formActions.on( 'click', '.frm_duplicate_form_action', copyFormAction );
9507 jQuery( document ).on( 'change', 'select[data-toggleclass], input[data-toggleclass]', toggleFormOpts );
9508 jQuery( '.frm_actions_list' ).on( 'click', '.frm_active_action', addFormAction );
9509 jQuery( '#frm-show-groups, #frm-hide-groups' ).on( 'click', toggleActionGroups );
9510 initiateMultiselect();
9511
9512 //set actions icons to inactive
9513 jQuery( 'ul.frm_actions_list li' ).each( function() {
9514 checkActiveAction( jQuery( this ).children( 'a' ).data( 'actiontype' ) );
9515
9516 // If the icon is a background image, don't add BG color.
9517 const icon = jQuery( this ).find( 'i' );
9518 if ( icon.css( 'background-image' ) !== 'none' ) {
9519 icon.addClass( 'frm-inverse' );
9520 }
9521 });
9522
9523 jQuery( '.frm_submit_settings_btn' ).on( 'click', submitSettings );
9524
9525 formSettings = jQuery( '.frm_form_settings' );
9526 formSettings.on( 'click', '.frm_add_form_logic', addFormLogicRow );
9527 formSettings.on( 'blur', '.frm_email_blur', formatEmailSetting );
9528 formSettings.on( 'click', '.frm_already_used', onlyOneActionMessage );
9529
9530 formSettings.on( 'change', '#logic_link_submit', toggleSubmitLogic );
9531 formSettings.on( 'click', '.frm_add_submit_logic', addSubmitLogic );
9532 formSettings.on( 'change', '.frm_submit_logic_field_opts', addSubmitLogicOpts );
9533
9534 document.addEventListener(
9535 'click',
9536 function handleImageUploadClickEvents( event ) {
9537 const { target } = event;
9538
9539 if ( ! target.closest( '.frm_image_preview_wrapper' ) ) {
9540 return;
9541 }
9542
9543 if ( target.closest( '.frm_choose_image_box' ) ) {
9544 addImageToOption.bind( target )( event );
9545 return;
9546 }
9547
9548 if ( target.closest( '.frm_remove_image_option' ) ) {
9549 removeImageFromOption.bind( target )( event );
9550 }
9551 }
9552 );
9553
9554 // Close shortcode modal on click.
9555 formSettings.on( 'mouseup', '*:not(.frm-show-box)', function( e ) {
9556 e.stopPropagation();
9557
9558 if ( e.target.classList.contains( 'frm-show-box' ) ) {
9559 return;
9560 }
9561
9562 const sidebar = document.getElementById( 'frm_adv_info' );
9563 if ( ! sidebar ) {
9564 return;
9565 }
9566
9567 if ( sidebar.getAttribute( 'data-fills' ) === e.target.id && typeof e.target.id !== 'undefined' ) {
9568 return;
9569 }
9570
9571 const isChild = jQuery( e.target ).closest( '#frm_adv_info' ).length > 0;
9572 if ( ! isChild && sidebar.display !== 'none' ) {
9573 hideShortcodes( sidebar );
9574 }
9575 });
9576
9577 //Warning when user selects "Do not store entries ..."
9578 jQuery( document.getElementById( 'no_save' ) ).on( 'change', function() {
9579 if ( this.checked ) {
9580 if ( confirm( frm_admin_js.no_save_warning ) !== true ) {
9581 // Uncheck box if user hits "Cancel"
9582 jQuery( this ).attr( 'checked', false );
9583 }
9584 }
9585 });
9586
9587 //Show/hide Messages header
9588 jQuery( '#editable, #edit_action, #save_draft, #success_action' ).on( 'change', function() {
9589 maybeShowFormMessages();
9590 });
9591 jQuery( 'select[name="options[success_action]"], select[name="options[edit_action]"]' ).on( 'change', showSuccessOpt );
9592
9593 $loggedIn = document.getElementById( 'logged_in' );
9594 jQuery( $loggedIn ).on( 'change', function() {
9595 if ( this.checked ) {
9596 visible( '.hide_logged_in' );
9597 } else {
9598 invisible( '.hide_logged_in' );
9599 }
9600 });
9601
9602 $cookieExp = jQuery( document.getElementById( 'frm_cookie_expiration' ) );
9603 jQuery( document.getElementById( 'frm_single_entry_type' ) ).on( 'change', function() {
9604 if ( this.value === 'cookie' ) {
9605 $cookieExp.fadeIn( 'slow' );
9606 } else {
9607 $cookieExp.fadeOut( 'slow' );
9608 }
9609 });
9610
9611 var $singleEntry = document.getElementById( 'single_entry' );
9612 jQuery( $singleEntry ).on( 'change', function() {
9613 if ( this.checked ) {
9614 visible( '.hide_single_entry' );
9615 } else {
9616 invisible( '.hide_single_entry' );
9617 }
9618
9619 if ( this.checked && jQuery( document.getElementById( 'frm_single_entry_type' ) ).val() === 'cookie' ) {
9620 $cookieExp.fadeIn( 'slow' );
9621 } else {
9622 $cookieExp.fadeOut( 'slow' );
9623 }
9624 });
9625
9626 jQuery( '.hide_save_draft' ).hide();
9627
9628 var $saveDraft = jQuery( document.getElementById( 'save_draft' ) );
9629 $saveDraft.on( 'change', function() {
9630 if ( this.checked ) {
9631 jQuery( '.hide_save_draft' ).fadeIn( 'slow' );
9632 } else {
9633 jQuery( '.hide_save_draft' ).fadeOut( 'slow' );
9634 }
9635 });
9636 triggerChange( $saveDraft );
9637
9638 //If Allow editing is checked/unchecked
9639 $editable = document.getElementById( 'editable' );
9640 jQuery( $editable ).on( 'change', function() {
9641 if ( this.checked ) {
9642 jQuery( '.hide_editable' ).fadeIn( 'slow' );
9643 triggerChange( document.getElementById( 'edit_action' ) );
9644 } else {
9645 jQuery( '.hide_editable' ).fadeOut( 'slow' );
9646 jQuery( '.edit_action_message_box' ).fadeOut( 'slow' );//Hide On Update message box
9647 }
9648 });
9649
9650 //If File Protection is checked/unchecked
9651 jQuery( document ).on( 'change', '#protect_files', function() {
9652 if ( this.checked ) {
9653 jQuery( '.hide_protect_files' ).fadeIn( 'slow' );
9654 } else {
9655 jQuery( '.hide_protect_files' ).fadeOut( 'slow' );
9656 }
9657 });
9658
9659 jQuery( document ).on( 'frm-multiselect-changed', '#protect_files_role', adjustVisibilityValuesForEveryoneValues );
9660
9661 jQuery( document ).on( 'submit', '.frm_form_settings', settingsSubmitted );
9662 jQuery( document ).on( 'change', '#form_settings_page input:not(.frm-search-input), #form_settings_page select, #form_settings_page textarea', fieldUpdated );
9663
9664 // Page Selection Autocomplete
9665 initSelectionAutocomplete();
9666 },
9667
9668 panelInit: function() {
9669 var customPanel, settingsPage, viewPage, insertFieldsTab;
9670
9671 jQuery( '.frm_wrap, #postbox-container-1' ).on( 'click', '.frm_insert_code', insertCode );
9672 jQuery( document ).on( 'change', '.frm_insert_val', function() {
9673 insertFieldCode( jQuery( this ).data( 'target' ), jQuery( this ).val() );
9674 jQuery( this ).val( '' );
9675 });
9676
9677 jQuery( document ).on( 'click change', '#frm-id-key-condition', resetLogicBuilder );
9678 jQuery( document ).on( 'keyup change', '.frm-build-logic', setLogicExample );
9679
9680 showInputIcon();
9681 jQuery( document ).on( 'frmElementAdded', function( event, parentEle ) {
9682 /* This is here for add-ons to trigger */
9683 showInputIcon( parentEle );
9684 });
9685 jQuery( document ).on( 'mousedown', '.frm-show-box', showShortcodes );
9686
9687 settingsPage = document.getElementById( 'form_settings_page' );
9688 viewPage = document.body.classList.contains( 'post-type-frm_display' );
9689 insertFieldsTab = document.getElementById( 'frm_insert_fields_tab' );
9690
9691 if ( settingsPage !== null || viewPage ) {
9692 jQuery( document ).on( 'focusin', 'form input, form textarea', function( e ) {
9693 var htmlTab;
9694 e.stopPropagation();
9695 maybeShowModal( this );
9696
9697 if ( jQuery( this ).is( ':not(:submit, input[type=button], .frm-search-input, input[type=checkbox])' ) ) {
9698 if ( jQuery( e.target ).closest( '#frm_adv_info' ).length ) {
9699 // Don't trigger for fields inside of the modal.
9700 return;
9701 }
9702
9703 if ( settingsPage !== null ) {
9704 /* form settings page */
9705 htmlTab = jQuery( '#frm_html_tab' );
9706 if ( jQuery( this ).closest( '#html_settings' ).length > 0 ) {
9707 htmlTab.show();
9708 htmlTab.siblings().hide();
9709 jQuery( '#frm_html_tab a' ).trigger( 'click' );
9710 toggleAllowedHTML( this, e.type );
9711 } else {
9712 showElement( jQuery( '.frm-category-tabs li' ) );
9713 insertFieldsTab.click();
9714 htmlTab.hide();
9715 htmlTab.siblings().show();
9716 }
9717 } else if ( viewPage ) {
9718 // Run on view page.
9719 toggleAllowedShortcodes( this.id, e.type );
9720 }
9721 }
9722 });
9723 }
9724
9725 jQuery( '.frm_wrap, #postbox-container-1' ).on( 'mousedown', '#frm_adv_info a, .frm_field_list a', function( e ) {
9726 e.preventDefault();
9727 });
9728
9729 customPanel = jQuery( '#frm_adv_info' );
9730 customPanel.on( 'click', '.subsubsub a.frmids', function( e ) {
9731 toggleKeyID( 'frmids', e );
9732 });
9733 customPanel.on( 'click', '.subsubsub a.frmkeys', function( e ) {
9734 toggleKeyID( 'frmkeys', e );
9735 });
9736 },
9737
9738 viewInit: function() {
9739 var $addRemove,
9740 $advInfo = jQuery( document.getElementById( 'frm_adv_info' ) );
9741 $advInfo.before( '<div id="frm_position_ele"></div>' );
9742 setupMenuOffset();
9743
9744 jQuery( document ).on( 'blur', '#param', checkDetailPageSlug );
9745 jQuery( document ).on( 'blur', 'input[name^="options[where_val]"]', checkFilterParamNames );
9746
9747 // Show loading indicator.
9748 jQuery( '#publish' ).on( 'mousedown', function() {
9749 fieldsUpdated = 0;
9750 this.classList.add( 'frm_loading_button' );
9751 });
9752
9753 // move content tabs
9754 jQuery( '#frm_dyncontent .handlediv' ).before( jQuery( '#frm_dyncontent .nav-menus-php' ) );
9755
9756 // click content tabs
9757 jQuery( '.nav-tab-wrapper a' ).on( 'click', clickContentTab );
9758
9759 // click tabs after panel is replaced with ajax
9760 jQuery( '#side-sortables' ).on( 'click', '.frm_doing_ajax.categorydiv .category-tabs a', clickTabsAfterAjax );
9761
9762 initToggleShortcodes();
9763 jQuery( '.frm_code_list:not(.frm-dropdown-menu) a' ).addClass( 'frm_noallow' );
9764
9765 jQuery( 'input[name="show_count"]' ).on( 'change', showCount );
9766
9767 jQuery( document.getElementById( 'form_id' ) ).on( 'change', displayFormSelected );
9768
9769 $addRemove = jQuery( '.frm_repeat_rows' );
9770 $addRemove.on( 'click', '.frm_add_order_row', addOrderRow );
9771 $addRemove.on( 'click', '.frm_add_where_row', addWhereRow );
9772 $addRemove.on( 'change', '.frm_insert_where_options', insertWhereOptions );
9773 $addRemove.on( 'change', '.frm_where_is_options', hideWhereOptions );
9774
9775 setDefaultPostStatus();
9776 },
9777
9778 inboxInit: function() {
9779 jQuery( '.frm_inbox_dismiss, footer .frm-button-secondary, footer .frm-button-primary' ).on( 'click', function( e ) {
9780 var message = this.parentNode.parentNode,
9781 key = message.getAttribute( 'data-message' ),
9782 href = this.getAttribute( 'href' );
9783
9784 if ( 'free_templates' === key && ! this.classList.contains( 'frm_inbox_dismiss' ) ) {
9785 return;
9786 }
9787
9788 e.preventDefault();
9789
9790 data = {
9791 action: 'frm_inbox_dismiss',
9792 key: key,
9793 nonce: frmGlobal.nonce
9794 };
9795 postAjax( data, function() {
9796 if ( href !== '#' ) {
9797 window.location = href;
9798 return true;
9799 }
9800 fadeOut( message, function() {
9801 message.parentNode.removeChild( message );
9802 });
9803 });
9804 });
9805 jQuery( '#frm-dismiss-inbox' ).on( 'click', function( e ) {
9806 data = {
9807 action: 'frm_inbox_dismiss',
9808 key: 'all',
9809 nonce: frmGlobal.nonce
9810 };
9811 postAjax( data, function() {
9812 fadeOut( document.getElementById( 'frm_message_list' ), function() {
9813 document.getElementById( 'frm_empty_inbox' ).classList.remove( 'frm_hidden' );
9814 });
9815 });
9816 });
9817 },
9818
9819 solutionInit: function() {
9820 jQuery( document ).on( 'submit', '#frm-new-template', installTemplate );
9821 },
9822
9823 styleInit: function() {
9824 const debouncedPreviewUpdate = debounce( changeStyling, 100 );
9825
9826 collapseAllSections();
9827
9828 document.getElementById( 'frm_field_height' ).addEventListener( 'change', textSquishCheck );
9829 document.getElementById( 'frm_field_font_size' ).addEventListener( 'change', textSquishCheck );
9830 document.getElementById( 'frm_field_pad' ).addEventListener( 'change', textSquishCheck );
9831
9832 jQuery( 'input.hex' ).wpColorPicker({
9833 change: function( event ) {
9834 if ( null !== event.target.getAttribute( 'data-alpha-color-type' ) ) {
9835 debouncedPreviewUpdate();
9836 return;
9837 } else {
9838 const hexcolor = jQuery( this ).wpColorPicker( 'color' );
9839 jQuery( event.target ).val( hexcolor ).trigger( 'change' );
9840 }
9841 }
9842 });
9843 jQuery( '.wp-color-result-text' ).text( function( i, oldText ) {
9844 return oldText === 'Select Color' ? 'Select' : oldText;
9845 });
9846
9847 function changeStyling() {
9848 var locStr = jQuery( 'input[name^="frm_style_setting[post_content]"], select[name^="frm_style_setting[post_content]"], textarea[name^="frm_style_setting[post_content]"], input[name="style_name"]' ).serializeArray();
9849 locStr = JSON.stringify( locStr );
9850 jQuery.ajax({
9851 type: 'POST', url: ajaxurl,
9852 data: {
9853 action: 'frm_change_styling',
9854 nonce: frmGlobal.nonce,
9855 frm_style_setting: locStr
9856 },
9857 success: function( css ) {
9858 document.getElementById( 'this_css' ).innerHTML = css;
9859 }
9860 });
9861 }
9862
9863 // update styling on change
9864 jQuery( '#frm_styling_form .styling_settings' ).on( 'change', debouncedPreviewUpdate );
9865
9866 // menu tabs
9867 jQuery( '#menu-settings-column' ).on( 'click', function( e ) {
9868 var panelId, wrapper,
9869 target = jQuery( e.target );
9870
9871 if ( e.target.className.indexOf( 'nav-tab-link' ) !== -1 ) {
9872
9873 panelId = target.data( 'type' );
9874
9875 wrapper = target.parents( '.accordion-section-content' ).first();
9876
9877
9878 jQuery( '.tabs-panel-active', wrapper ).removeClass( 'tabs-panel-active' ).addClass( 'tabs-panel-inactive' );
9879 jQuery( '#' + panelId, wrapper ).removeClass( 'tabs-panel-inactive' ).addClass( 'tabs-panel-active' );
9880
9881 jQuery( '.tabs', wrapper ).removeClass( 'tabs' );
9882 target.parent().addClass( 'tabs' );
9883
9884 // select the search bar
9885 jQuery( '.quick-search', wrapper ).trigger( 'focus' );
9886
9887 e.preventDefault();
9888 }
9889 });
9890
9891 jQuery( document ).on( 'change', '.frm-dropdown-menu input[type="radio"]', function() {
9892 const radio = this;
9893 const btnGrp = this.closest( '.btn-group' );
9894 const btnId = btnGrp.getAttribute( 'id' );
9895
9896 const select = document.getElementById( btnId.replace( '_select', '' ) );
9897 if ( select ) {
9898 select.value = radio.value;
9899 }
9900
9901 jQuery( btnGrp ).children( 'button' ).html( radio.nextElementSibling.innerHTML + ' <b class="caret"></b>' );
9902
9903 const activeItem = btnGrp.querySelector( '.dropdown-item.active' );
9904 if ( activeItem ) {
9905 activeItem.classList.remove( 'active' );
9906 }
9907
9908 this.closest( '.dropdown-item' ).classList.add( 'active' );
9909 });
9910
9911 jQuery( '#frm_confirm_modal' ).on( 'click', '[data-resetstyle]', function( e ) {
9912 var button = document.getElementById( 'frm_reset_style' );
9913
9914 button.classList.add( 'frm_loading_button' );
9915 e.stopPropagation();
9916
9917 jQuery.ajax({
9918 type: 'POST', url: ajaxurl,
9919 data: {action: 'frm_settings_reset', nonce: frmGlobal.nonce},
9920 success: function( errObj ) {
9921 var key;
9922 errObj = errObj.replace( /^\s+|\s+$/g, '' );
9923 if ( errObj.indexOf( '{' ) === 0 ) {
9924 errObj = JSON.parse( errObj );
9925 }
9926 for ( key in errObj ) {
9927 jQuery( 'input[name$="[' + key + ']"], select[name$="[' + key + ']"]' ).val( errObj[key]);
9928 }
9929 jQuery( '#frm_submit_style, #frm_auto_width' ).prop( 'checked', false );
9930 triggerChange( document.getElementById( 'frm_fieldset' ) );
9931 button.classList.remove( 'frm_loading_button' );
9932 }
9933 });
9934 });
9935
9936 jQuery( '.frm_pro_form #datepicker_sample' ).datepicker({ changeMonth: true, changeYear: true });
9937
9938 jQuery( document.getElementById( 'frm_position' ) ).on( 'change', setPosClass );
9939
9940 jQuery( '.frm_image_preview_wrapper' ).on( 'click', '.frm_choose_image_box', addImageToOption );
9941 jQuery( '.frm_image_preview_wrapper' ).on( 'click', '.frm_remove_image_option', removeImageFromOption );
9942
9943 // Check floating label when focus or blur fields.
9944 const floatingLabelSelector = '.frm_inside_container > input, .frm_inside_container > textarea, .frm_inside_container > select';
9945 [ 'focus', 'blur', 'change' ].forEach( function( eventName ) {
9946 documentOn(
9947 eventName,
9948 floatingLabelSelector,
9949 function( event ) {
9950 checkFloatingLabelsForStyles( event.target );
9951 },
9952 true
9953 );
9954 });
9955
9956 // Trigger label position option on load.
9957 const changeEvent = document.createEvent( 'HTMLEvents' );
9958 changeEvent.initEvent( 'change', true, false );
9959 document.getElementById( 'frm_position' ).dispatchEvent( changeEvent );
9960 },
9961
9962 customCSSInit: function() {
9963 console.warn( 'Calling frmAdminBuild.customCSSInit is deprecated.' );
9964 },
9965
9966 globalSettingsInit: function() {
9967 var licenseTab;
9968
9969 jQuery( document ).on( 'click', '[data-frmuninstall]', uninstallNow );
9970
9971 initiateMultiselect();
9972
9973 // activate addon licenses
9974 licenseTab = document.getElementById( 'licenses_settings' );
9975 if ( licenseTab !== null ) {
9976 jQuery( licenseTab ).on( 'click', '.edd_frm_save_license', saveAddonLicense );
9977 }
9978
9979 // Solution install page
9980 jQuery( document ).on( 'click', '#frm-new-template button', installTemplateFieldset );
9981
9982 jQuery( '#frm-dismissable-cta .dismiss' ).on( 'click', function( event ) {
9983 event.preventDefault();
9984 jQuery.post( ajaxurl, {
9985 action: 'frm_lite_settings_upgrade'
9986 });
9987 jQuery( '.settings-lite-cta' ).remove();
9988 });
9989
9990 const captchaType = document.getElementById( 'frm_re_type' );
9991 if ( captchaType ) {
9992 captchaType.addEventListener( 'change', handleCaptchaTypeChange );
9993 }
9994 },
9995
9996 exportInit: function() {
9997 jQuery( '.frm_form_importer' ).on( 'submit', startFormMigration );
9998 jQuery( document.getElementById( 'frm_export_xml' ) ).on( 'submit', validateExport );
9999 jQuery( '#frm_export_xml input, #frm_export_xml select' ).on( 'change', removeExportError );
10000 jQuery( 'input[name="frm_import_file"]' ).on( 'change', checkCSVExtension );
10001 jQuery( 'select[name="format"]' ).on( 'change', checkExportTypes ).trigger( 'change' );
10002 jQuery( 'input[name="frm_export_forms[]"]' ).on( 'click', preventMultipleExport );
10003 initiateMultiselect();
10004
10005 jQuery( '.frm-feature-banner .dismiss' ).on( 'click', function( event ) {
10006 event.preventDefault();
10007 jQuery.post( ajaxurl, {
10008 action: 'frm_dismiss_migrator',
10009 plugin: this.id,
10010 nonce: frmGlobal.nonce
10011 });
10012 this.parentElement.remove();
10013 });
10014 },
10015
10016 inboxBannerInit: function() {
10017 const banner = document.getElementById( 'frm_banner' );
10018 if ( ! banner ) {
10019 return;
10020 }
10021
10022 const dismissButton = banner.querySelector( '.frm-banner-dismiss' );
10023 document.addEventListener(
10024 'click',
10025 function( event ) {
10026 if ( event.target !== dismissButton ) {
10027 return;
10028 }
10029
10030 const data = {
10031 action: 'frm_inbox_dismiss',
10032 key: banner.dataset.key,
10033 nonce: frmGlobal.nonce
10034 };
10035 postAjax(
10036 data,
10037 function() {
10038 jQuery( banner ).fadeOut(
10039 400,
10040 function() {
10041 banner.remove();
10042 }
10043 );
10044 }
10045 );
10046 }
10047 );
10048 },
10049
10050 updateOpts: function( fieldId, opts, modal ) {
10051 var separate = usingSeparateValues( fieldId ),
10052 action = isProductField( fieldId ) ? 'frm_bulk_products' : 'frm_import_options';
10053 jQuery.ajax({
10054 type: 'POST',
10055 url: ajaxurl,
10056 data: {
10057 action: action,
10058 field_id: fieldId,
10059 opts: opts,
10060 separate: separate,
10061 nonce: frmGlobal.nonce
10062 },
10063 success: function( html ) {
10064 document.getElementById( 'frm_field_' + fieldId + '_opts' ).innerHTML = html;
10065 resetDisplayedOpts( fieldId );
10066
10067 if ( typeof modal !== 'undefined' ) {
10068 modal.dialog( 'close' );
10069 document.getElementById( 'frm-update-bulk-opts' ).classList.remove( 'frm_loading_button' );
10070 }
10071 }
10072 });
10073 },
10074
10075 /* remove conditional logic if the field doesn't exist */
10076 triggerRemoveLogic: function( fieldID, metaName ) {
10077 jQuery( '#frm_logic_' + fieldID + '_' + metaName + ' .frm_remove_tag' ).trigger( 'click' );
10078 },
10079
10080 downloadXML: function( controller, ids, isTemplate ) {
10081 var url = ajaxurl + '?action=frm_' + controller + '_xml&ids=' + ids;
10082 if ( isTemplate !== null ) {
10083 url = url + '&is_template=' + isTemplate;
10084 }
10085 location.href = url;
10086 },
10087
10088 /**
10089 * @since 5.0.04
10090 */
10091 hooks: {
10092 applyFilters: function( hookName, ...args ) {
10093 return wp.hooks.applyFilters( hookName, ...args );
10094 },
10095 addFilter: function( hookName, callback, priority ) {
10096 return wp.hooks.addFilter( hookName, 'formidable', callback, priority );
10097 }
10098 }
10099 };
10100 }
10101
10102 frmAdminBuild = frmAdminBuildJS();
10103
10104 jQuery( document ).ready(
10105 () => {
10106 frmAdminBuild.init();
10107
10108 frmDom.bootstrap.setupBootstrapDropdowns( convertOldBootstrapDropdownsToBootstrap4 );
10109 function convertOldBootstrapDropdownsToBootstrap4( frmDropdownMenu ) {
10110 const toggle = frmDropdownMenu.querySelector( '.frm-dropdown-toggle' );
10111 if ( toggle ) {
10112 if ( ! toggle.hasAttribute( 'role' ) ) {
10113 toggle.setAttribute( 'role', 'button' );
10114 }
10115 if ( ! toggle.hasAttribute( 'tabindex' ) ) {
10116 toggle.setAttribute( 'tabindex', 0 );
10117 }
10118 }
10119
10120 // Convert <li> and <ul> tags.
10121 if ( 'UL' === frmDropdownMenu.tagName ) {
10122 convertBootstrapUl( frmDropdownMenu );
10123 }
10124 }
10125
10126 function convertBootstrapUl( ul ) {
10127 let html = ul.outerHTML;
10128 html = html.replace( '<ul ', '<div ' );
10129 html = html.replace( '</ul>', '</div>' );
10130 html = html.replaceAll( '<li>', '<div class="dropdown-item">' );
10131 html = html.replaceAll( '<li class="', '<div class="dropdown-item ' );
10132 html = html.replaceAll( '</li>', '</div>' );
10133 ul.outerHTML = html;
10134 }
10135 }
10136 );
10137
10138 function frm_remove_tag( htmlTag ) { // eslint-disable-line camelcase
10139 console.warn( 'DEPRECATED: function frm_remove_tag in v2.0' );
10140 jQuery( htmlTag ).remove();
10141 }
10142
10143 function frm_show_div( div, value, showIf, classId ) { // eslint-disable-line camelcase
10144 if ( value == showIf ) {
10145 jQuery( classId + div ).fadeIn( 'slow' ).css( 'visibility', 'visible' );
10146 } else {
10147 jQuery( classId + div ).fadeOut( 'slow' );
10148 }
10149 }
10150
10151 function frmCheckAll( checked, n ) {
10152 jQuery( 'input[name^="' + n + '"]' ).prop( 'checked', ! ! checked );
10153 }
10154
10155 function frmCheckAllLevel( checked, n, level ) {
10156 var $kids = jQuery( '.frm_catlevel_' + level ).children( '.frm_checkbox' ).children( 'label' );
10157 $kids.children( 'input[name^="' + n + '"]' ).prop( 'checked', ! ! checked );
10158 }
10159
10160 function frm_add_logic_row( id, formId ) { // eslint-disable-line camelcase
10161 console.warn( 'DEPRECATED: function frm_add_logic_row in v2.0' );
10162 jQuery.ajax({
10163 type: 'POST',
10164 url: ajaxurl,
10165 data: {
10166 action: 'frm_add_logic_row',
10167 form_id: formId,
10168 field_id: id,
10169 meta_name: jQuery( '#frm_logic_row_' + id + ' > div' ).length,
10170 nonce: frmGlobal.nonce
10171 },
10172 success: function( html ) {
10173 jQuery( '#frm_logic_row_' + id ).append( html );
10174 }
10175 });
10176 return false;
10177 }
10178
10179 function frmGetFieldValues( fieldId, cur, rowNumber, fieldType, htmlName ) {
10180
10181 if ( fieldId ) {
10182 jQuery.ajax({
10183 type: 'POST', url: ajaxurl,
10184 data: 'action=frm_get_field_values&current_field=' + cur + '&field_id=' + fieldId + '&name=' + htmlName + '&t=' + fieldType + '&form_action=' + jQuery( 'input[name="frm_action"]' ).val() + '&nonce=' + frmGlobal.nonce,
10185 success: function( msg ) {
10186 document.getElementById( 'frm_show_selected_values_' + cur + '_' + rowNumber ).innerHTML = msg;
10187 }
10188 });
10189 }
10190 }
10191
10192 function frmImportCsv( formID ) {
10193 var urlVars = '';
10194 if ( typeof __FRMURLVARS !== 'undefined' ) {
10195 urlVars = __FRMURLVARS;
10196 }
10197
10198 jQuery.ajax({
10199 type: 'POST', url: ajaxurl,
10200 data: 'action=frm_import_csv&nonce=' + frmGlobal.nonce + '&frm_skip_cookie=1' + urlVars,
10201 success: function( count ) {
10202 var max = jQuery( '.frm_admin_progress_bar' ).attr( 'aria-valuemax' );
10203 var imported = max - count;
10204 var percent = ( imported / max ) * 100;
10205 jQuery( '.frm_admin_progress_bar' ).css( 'width', percent + '%' ).attr( 'aria-valuenow', imported );
10206
10207 if ( parseInt( count, 10 ) > 0 ) {
10208 jQuery( '.frm_csv_remaining' ).html( count );
10209 frmImportCsv( formID );
10210 } else {
10211 jQuery( document.getElementById( 'frm_import_message' ) ).html( frm_admin_js.import_complete );
10212 setTimeout( function() {
10213 location.href = '?page=formidable-entries&frm_action=list&form=' + formID + '&import-message=1';
10214 }, 2000 );
10215 }
10216 }
10217 });
10218 }
10219