PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 5.3
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v5.3
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.3, at js/formidable_admin.js

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