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

9,839 lines 297.1 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 if ( modal.querySelector( '.frm-ready-made-solution' ) ) {
7109 // Avoid adding duplicates if the modal is opened multiple times.
7110 return;
7111 }
7112
7113 if ( ! frmGlobal.canAccessApplicationDashboard ) {
7114 // User does not have privileges to see Ready Made Solutions.
7115 return;
7116 }
7117
7118 doJsonFetch( 'get_applications_data&view=templates' ).then( addTemplatesOnFetchSuccess );
7119
7120 const categoryList = modal.querySelector( 'ul.frm-categories-list' );
7121
7122 function addTemplatesOnFetchSuccess( data ) {
7123 data.templates.forEach( addTemplateToCategoryList );
7124 }
7125
7126 function addTemplateToCategoryList( template ) {
7127 categoryList.insertBefore( getReadyMadeSolution( template ), categoryList.firstChild );
7128 }
7129
7130 function getReadyMadeSolution( template ) {
7131 const image = tag( 'img' );
7132 const thumbnailFolderUrl = frmGlobal.url + '/images/applications/thumbnails/';
7133 const filenameToUse = template.hasLiteThumbnail ? template.key + '.png' : 'placeholder.svg';
7134 image.setAttribute( 'src', thumbnailFolderUrl + filenameToUse );
7135
7136 const imageWrapper = div({ child: image });
7137 imageWrapper.style.textAlign = 'center';
7138
7139 return tag(
7140 'li',
7141 {
7142 className: 'frm-searchable-template frm-ready-made-solution',
7143 children: [
7144 imageWrapper,
7145 div({
7146 children: [
7147 span( __( 'Ready Made Solution', 'formidable' ) ),
7148 tag( 'h3', template.name ),
7149 a({
7150 text: __( 'Check all applications', 'formidable' ),
7151 href: frmGlobal.applicationsUrl
7152 })
7153 ]
7154 }),
7155 div({
7156 className: 'frm-hover-icons',
7157 child: a({
7158 child: svg({ href: '#frm_plus_icon' }),
7159 href: frmGlobal.applicationsUrl + '&triggerViewApplicationModal=1&template=' + template.key
7160 })
7161 })
7162 ]
7163 }
7164 );
7165 }
7166 }
7167
7168 function offsetModalY( $modal, amount ) {
7169 const position = {
7170 my: 'top',
7171 at: 'top+' + amount,
7172 of: window
7173 };
7174 $modal.dialog( 'option', 'position', position );
7175 }
7176
7177 /**
7178 * Get the input box for the selected ... icon.
7179 */
7180 function getInputForIcon( moreIcon ) {
7181 var input = moreIcon.nextElementSibling;
7182 if ( input !== null && input.tagName !== 'INPUT' && input.tagName !== 'TEXTAREA' ) {
7183 // Workaround for 1Password.
7184 input = input.nextElementSibling;
7185 }
7186 return input;
7187 }
7188
7189 /**
7190 * Get the ... icon for the selected input box.
7191 */
7192 function getIconForInput( input ) {
7193 var moreIcon = input.previousElementSibling;
7194 if ( moreIcon !== null && moreIcon.tagName !== 'I' && moreIcon.tagName !== 'svg' ) {
7195 moreIcon = moreIcon.previousElementSibling;
7196 }
7197 return moreIcon;
7198 }
7199
7200 function hideShortcodes( box ) {
7201 var i, u, closeIcons, closeSvg;
7202 if ( typeof box === 'undefined' ) {
7203 box = document.getElementById( 'frm_adv_info' );
7204 if ( box === null ) {
7205 return;
7206 }
7207 }
7208
7209 if ( document.getElementById( 'frm_dyncontent' ) !== null ) {
7210 // Don't run when in the sidebar.
7211 return;
7212 }
7213
7214 box.style.display = 'none';
7215
7216 closeIcons = document.querySelectorAll( '.frm-show-box.frm_close_icon' );
7217 for ( i = 0; i < closeIcons.length; i++ ) {
7218 closeIcons[i].classList.remove( 'frm_close_icon' );
7219 closeIcons[i].classList.add( 'frm_more_horiz_solid_icon' );
7220 }
7221
7222 closeSvg = document.querySelectorAll( '.frm_has_shortcodes use' );
7223 for ( u = 0; u < closeSvg.length; u++ ) {
7224 if ( closeSvg[u].getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' ) === '#frm_close_icon' ) {
7225 closeSvg[u].setAttributeNS( 'http://www.w3.org/1999/xlink', 'href', '#frm_more_horiz_solid_icon' );
7226 }
7227 }
7228 }
7229
7230 function initToggleShortcodes() {
7231 if ( typeof tinymce !== 'object' ) {
7232 return;
7233 }
7234
7235 DOM = tinymce.DOM;
7236 if ( typeof DOM.events !== 'undefined' && typeof DOM.events.add !== 'undefined' ) {
7237 DOM.events.add( DOM.select( '.wp-editor-wrap' ), 'mouseover', function() {
7238 if ( jQuery( '*:focus' ).length > 0 ) {
7239 return;
7240 }
7241 if ( this.id ) {
7242 toggleAllowedShortcodes( this.id.slice( 3, -5 ), 'focusin' );
7243 }
7244 });
7245 DOM.events.add( DOM.select( '.wp-editor-wrap' ), 'mouseout', function() {
7246 if ( jQuery( '*:focus' ).length > 0 ) {
7247 return;
7248 }
7249 if ( this.id ) {
7250 toggleAllowedShortcodes( this.id.slice( 3, -5 ), 'focusin' );
7251 }
7252 });
7253 } else {
7254 jQuery( '#frm_dyncontent' ).on( 'mouseover mouseout', '.wp-editor-wrap', function() {
7255 if ( jQuery( '*:focus' ).length > 0 ) {
7256 return;
7257 }
7258 if ( this.id ) {
7259 toggleAllowedShortcodes( this.id.slice( 3, -5 ), 'focusin' );
7260 }
7261 });
7262 }
7263 }
7264
7265 function toggleAllowedShortcodes( id ) {
7266 var c, clickedID;
7267 if ( typeof id === 'undefined' ) {
7268 id = '';
7269 }
7270 c = id;
7271
7272 if ( id.indexOf( '-search-input' ) !== -1 ) {
7273 return;
7274 }
7275
7276 if ( id !== '' ) {
7277 var $ele = jQuery( document.getElementById( id ) );
7278 if ( $ele.attr( 'class' ) && id !== 'wpbody-content' && id !== 'content' && id !== 'dyncontent' && id !== 'success_msg' ) {
7279 var d = $ele.attr( 'class' ).split( ' ' )[0];
7280 if ( d === 'frm_long_input' || d === 'frm_98_width' || typeof d === 'undefined' ) {
7281 d = '';
7282 } else {
7283 id = d.trim();
7284 }
7285 c = c + ' ' + d;
7286 c = c.replace( 'widefat', '' ).replace( 'frm_with_left_label', '' );
7287 }
7288 }
7289
7290 jQuery( '#frm-insert-fields-box,#frm-conditionals,#frm-adv-info-tab,#frm-dynamic-values' ).attr( 'data-fills', c.trim() );
7291 var a = [
7292 'content', 'wpbody-content', 'dyncontent', 'success_url',
7293 'success_msg', 'edit_msg', 'frm_dyncontent', 'frm_not_email_message',
7294 'frm_not_email_subject'
7295 ];
7296 var b = [
7297 'before_content', 'after_content', 'frm_not_email_to',
7298 'dyn_default_value'
7299 ];
7300
7301 if ( jQuery.inArray( id, a ) >= 0 ) {
7302 jQuery( '.frm_code_list a' ).removeClass( 'frm_noallow' ).addClass( 'frm_allow' );
7303 jQuery( '.frm_code_list a.hide_' + id ).addClass( 'frm_noallow' ).removeClass( 'frm_allow' );
7304 } else if ( jQuery.inArray( id, b ) >= 0 ) {
7305 jQuery( '.frm_code_list:not(.frm-dropdown-menu) a:not(.show_' + id + ')' ).addClass( 'frm_noallow' ).removeClass( 'frm_allow' );
7306 jQuery( '.frm_code_list a.show_' + id ).removeClass( 'frm_noallow' ).addClass( 'frm_allow' );
7307 } else {
7308 jQuery( '.frm_code_list:not(.frm-dropdown-menu) a' ).addClass( 'frm_noallow' ).removeClass( 'frm_allow' );
7309 }
7310
7311 // Automatically select a tab.
7312 if ( id === 'dyn_default_value' ) {
7313 clickedID = 'frm_dynamic_values';
7314 document.getElementById( clickedID + '_tab' ).click();
7315 jQuery( '#' + clickedID.replace( /_/g, '-' ) + ' .frm_show_inactive' ).addClass( 'frm_hidden' );
7316 jQuery( '#' + clickedID.replace( /_/g, '-' ) + ' .frm_show_active' ).removeClass( 'frm_hidden' );
7317 }
7318 }
7319
7320 function toggleAllowedHTML( input ) {
7321 var b,
7322 id = input.id;
7323 if ( typeof id === 'undefined' || id.indexOf( '-search-input' ) !== -1 ) {
7324 return;
7325 }
7326
7327 jQuery( '#frm-adv-info-tab' ).attr( 'data-fills', id.trim() );
7328 if ( input.classList.contains( 'field_custom_html' ) ) {
7329 id = 'field_custom_html';
7330 }
7331
7332 b = [ 'after_html', 'before_html', 'submit_html', 'field_custom_html' ];
7333 if ( jQuery.inArray( id, b ) >= 0 ) {
7334 jQuery( '.frm_code_list li:not(.show_' + id + ')' ).addClass( 'frm_hidden' );
7335 jQuery( '.frm_code_list li.show_' + id ).removeClass( 'frm_hidden' );
7336 }
7337 }
7338
7339 function toggleKeyID( switchTo, e ) {
7340 e.stopPropagation();
7341 jQuery( '.frm_code_list .frmids, .frm_code_list .frmkeys' ).addClass( 'frm_hidden' );
7342 jQuery( '.frm_code_list .' + switchTo ).removeClass( 'frm_hidden' );
7343 jQuery( '.frmids, .frmkeys' ).removeClass( 'current' );
7344 jQuery( '.' + switchTo ).addClass( 'current' );
7345 }
7346
7347 /* Styling */
7348 function setPosClass() {
7349 /*jshint validthis:true */
7350 var value = this.value;
7351 if ( value === 'none' ) {
7352 value = 'top';
7353 } else if ( value === 'no_label' ) {
7354 value = 'none';
7355 }
7356 jQuery( '.frm_pos_container' ).removeClass( 'frm_top_container frm_left_container frm_right_container frm_none_container frm_inside_container' ).addClass( 'frm_' + value + '_container' );
7357 }
7358
7359 function collapseAllSections() {
7360 jQuery( '.control-section.accordion-section.open' ).removeClass( 'open' );
7361 }
7362
7363 function textSquishCheck() {
7364 var size = document.getElementById( 'frm_field_font_size' ).value.replace( /\D/g, '' );
7365 var height = document.getElementById( 'frm_field_height' ).value.replace( /\D/g, '' );
7366 var paddingEntered = document.getElementById( 'frm_field_pad' ).value.split( ' ' );
7367 var paddingCount = paddingEntered.length;
7368
7369 // If too many or too few padding entries, leave now
7370 if ( paddingCount === 0 || paddingCount > 4 || height === '' ) {
7371 return;
7372 }
7373
7374 // Get the top and bottom padding from entered values
7375 var paddingTop = paddingEntered[0].replace( /\D/g, '' );
7376 var paddingBottom = paddingTop;
7377 if ( paddingCount >= 3 ) {
7378 paddingBottom = paddingEntered[2].replace( /\D/g, '' );
7379 }
7380
7381 // Check if there is enough space for text
7382 var textSpace = height - size - paddingTop - paddingBottom - 3;
7383 if ( textSpace < 0 ) {
7384 infoModal( frm_admin_js.css_invalid_size );
7385 }
7386 }
7387
7388 /* Global settings page */
7389 function loadSettingsTab( anchor ) {
7390 var holder = anchor.replace( '#', '' );
7391 var holderContainer = jQuery( '.frm_' + holder + '_ajax' );
7392 if ( holderContainer.length ) {
7393 jQuery.ajax({
7394 type: 'POST', url: ajaxurl,
7395 data: {
7396 'action': 'frm_settings_tab',
7397 'tab': holder.replace( '_settings', '' ),
7398 'nonce': frmGlobal.nonce
7399 },
7400 success: function( html ) {
7401 holderContainer.replaceWith( html );
7402 }
7403 });
7404 }
7405 }
7406
7407 function uninstallNow() {
7408 /*jshint validthis:true */
7409 if ( confirmLinkClick( this ) === true ) {
7410 jQuery( '.frm_uninstall .frm-wait' ).css( 'visibility', 'visible' );
7411 jQuery.ajax({
7412 type: 'POST',
7413 url: ajaxurl,
7414 data: 'action=frm_uninstall&nonce=' + frmGlobal.nonce,
7415 success: function( msg ) {
7416 jQuery( '.frm_uninstall' ).fadeOut( 'slow' );
7417 window.location = msg;
7418 }
7419 });
7420 }
7421 return false;
7422 }
7423
7424 function saveAddonLicense() {
7425 /*jshint validthis:true */
7426 var button = jQuery( this );
7427 var buttonName = this.name;
7428 var pluginSlug = this.getAttribute( 'data-plugin' );
7429 var action = buttonName.replace( 'edd_' + pluginSlug + '_license_', '' );
7430 var license = document.getElementById( 'edd_' + pluginSlug + '_license_key' ).value;
7431 jQuery.ajax({
7432 type: 'POST', url: ajaxurl, dataType: 'json',
7433 data: {action: 'frm_addon_' + action, license: license, plugin: pluginSlug, nonce: frmGlobal.nonce},
7434 success: function( msg ) {
7435 var thisRow = button.closest( '.edd_frm_license_row' );
7436 if ( action === 'deactivate' ) {
7437 license = '';
7438 document.getElementById( 'edd_' + pluginSlug + '_license_key' ).value = '';
7439 }
7440 thisRow.find( '.edd_frm_license' ).html( license );
7441 if ( msg.success === true ) {
7442 thisRow.find( '.frm_icon_font' ).removeClass( 'frm_hidden' );
7443 thisRow.find( 'div.alignleft' ).toggleClass( 'frm_hidden', 1000 );
7444 }
7445
7446 var messageBox = thisRow.find( '.frm_license_msg' );
7447 messageBox.html( msg.message );
7448 if ( msg.message !== '' ) {
7449 setTimeout( function() {
7450 messageBox.html( '' );
7451 }, 15000 );
7452 }
7453 }
7454 });
7455 }
7456
7457 /* Import/Export page */
7458
7459 function startFormMigration( event ) {
7460 event.preventDefault();
7461
7462 var checkedBoxes = jQuery( event.target ).find( 'input:checked' );
7463 if ( ! checkedBoxes.length ) {
7464 return;
7465 }
7466
7467 var ids = [];
7468 checkedBoxes.each( function( i ) {
7469 ids[i] = this.value;
7470 });
7471
7472 // Begin the import process.
7473 importForms( ids, event.target );
7474 }
7475
7476 /**
7477 * Begins the process of importing the forms.
7478 */
7479 function importForms( forms, targetForm ) {
7480
7481 // Hide the form select section.
7482 var $form = jQuery( targetForm ),
7483 $processSettings = $form.next( '.frm-importer-process' );
7484
7485 // Display total number of forms we have to import.
7486 $processSettings.find( '.form-total' ).text( forms.length );
7487 $processSettings.find( '.form-current' ).text( '1' );
7488
7489 $form.hide();
7490
7491 // Show processing status.
7492 // '.process-completed' might have been shown earlier during a previous import, so hide now.
7493 $processSettings.find( '.process-completed' ).hide();
7494 $processSettings.show();
7495
7496 // Create global import queue.
7497 s.importQueue = forms;
7498 s.imported = 0;
7499
7500 // Import the first form in the queue.
7501 importForm( $processSettings );
7502 }
7503
7504 /**
7505 * Imports a single form from the import queue.
7506 */
7507 function importForm( $processSettings ) {
7508 var formID = s.importQueue[0],
7509 provider = $processSettings.closest( '.welcome-panel-content' ).find( 'input[name="slug"]' ).val(),
7510 data = {
7511 action: 'frm_import_' + provider,
7512 form_id: formID,
7513 nonce: frmGlobal.nonce
7514 };
7515
7516 // Trigger AJAX import for this form.
7517 jQuery.post( ajaxurl, data, function( res ) {
7518
7519 if ( res.success ) {
7520 var statusUpdate;
7521
7522 if ( res.data.error ) {
7523 statusUpdate = '<p>' + res.data.name + ': ' + res.data.msg + '</p>';
7524 } else {
7525 statusUpdate = '<p>Imported <a href="' + res.data.link + '" target="_blank">' + res.data.name + '</a></p>';
7526 }
7527
7528 $processSettings.find( '.status' ).prepend( statusUpdate );
7529 $processSettings.find( '.status' ).show();
7530
7531 // Remove this form ID from the queue.
7532 s.importQueue = jQuery.grep( s.importQueue, function( value ) {
7533 return value != formID;
7534 });
7535 s.imported++;
7536
7537 if ( s.importQueue.length === 0 ) {
7538 $processSettings.find( '.process-count' ).hide();
7539 $processSettings.find( '.forms-completed' ).text( s.imported );
7540 $processSettings.find( '.process-completed' ).show();
7541 } else {
7542 // Import next form in the queue.
7543 $processSettings.find( '.form-current' ).text( s.imported + 1 );
7544 importForm( $processSettings );
7545 }
7546 }
7547 });
7548 }
7549
7550 function validateExport( e ) {
7551 /*jshint validthis:true */
7552 e.preventDefault();
7553
7554 var s = false;
7555 var $exportForms = jQuery( 'input[name="frm_export_forms[]"]' );
7556
7557 if ( ! jQuery( 'input[name="frm_export_forms[]"]:checked' ).val() ) {
7558 $exportForms.closest( '.frm-table-box' ).addClass( 'frm_blank_field' );
7559 s = 'stop';
7560 }
7561
7562 var $exportType = jQuery( 'input[name="type[]"]' );
7563 if ( ! jQuery( 'input[name="type[]"]:checked' ).val() && $exportType.attr( 'type' ) === 'checkbox' ) {
7564 $exportType.closest( 'p' ).addClass( 'frm_blank_field' );
7565 s = 'stop';
7566 }
7567
7568 if ( s === 'stop' ) {
7569 return false;
7570 }
7571
7572 e.stopPropagation();
7573 this.submit();
7574 }
7575
7576 function removeExportError() {
7577 /*jshint validthis:true */
7578 var t = jQuery( this ).closest( '.frm_blank_field' );
7579 if ( typeof t === 'undefined' ) {
7580 return;
7581 }
7582
7583 var $thisName = this.name;
7584 if ( $thisName === 'type[]' && jQuery( 'input[name="type[]"]:checked' ).val() ) {
7585 t.removeClass( 'frm_blank_field' );
7586 } else if ( $thisName === 'frm_export_forms[]' && jQuery( this ).val() ) {
7587 t.removeClass( 'frm_blank_field' );
7588 }
7589
7590 }
7591
7592 function checkCSVExtension() {
7593 /*jshint validthis:true */
7594 var f = jQuery( this ).val();
7595 var re = /\.csv$/i;
7596 if ( f.match( re ) !== null ) {
7597 jQuery( '.show_csv' ).fadeIn();
7598 } else {
7599 jQuery( '.show_csv' ).fadeOut();
7600 }
7601 }
7602
7603 function checkExportTypes() {
7604 /*jshint validthis:true */
7605 var $dropdown = jQuery( this );
7606 var $selected = $dropdown.find( ':selected' );
7607 var s = $selected.data( 'support' );
7608
7609 var multiple = s.indexOf( '|' );
7610 jQuery( 'input[name="type[]"]' ).each( function() {
7611 this.checked = false;
7612 if ( s.indexOf( this.value ) >= 0 ) {
7613 this.disabled = false;
7614 if ( multiple === -1 ) {
7615 this.checked = true;
7616 }
7617 } else {
7618 this.disabled = true;
7619 }
7620 });
7621
7622 if ( $dropdown.val() === 'csv' ) {
7623 jQuery( '.csv_opts' ).show();
7624 jQuery( '.xml_opts' ).hide();
7625 } else {
7626 jQuery( '.csv_opts' ).hide();
7627 jQuery( '.xml_opts' ).show();
7628 }
7629
7630 var c = $selected.data( 'count' );
7631 var exportField = jQuery( 'input[name="frm_export_forms[]"]' );
7632 if ( c === 'single' ) {
7633 exportField.prop( 'multiple', false );
7634 exportField.prop( 'checked', false );
7635 } else {
7636 exportField.prop( 'multiple', true );
7637 exportField.prop( 'disabled', false );
7638 }
7639 }
7640
7641 function preventMultipleExport() {
7642 var type = jQuery( 'select[name=format]' ),
7643 selected = type.find( ':selected' ),
7644 count = selected.data( 'count' ),
7645 exportField = jQuery( 'input[name="frm_export_forms[]"]' );
7646
7647 if ( count === 'single' ) {
7648 // Disable all other fields to prevent multiple selections.
7649 if ( this.checked ) {
7650 exportField.prop( 'disabled', true );
7651 this.removeAttribute( 'disabled' );
7652 } else {
7653 exportField.prop( 'disabled', false );
7654 }
7655 } else {
7656 exportField.prop( 'disabled', false );
7657 }
7658 }
7659
7660 function initiateMultiselect() {
7661 jQuery( '.frm_multiselect' ).hide().each( frmDom.bootstrap.multiselect.init );
7662 }
7663
7664 /* Addons page */
7665 function installMultipleAddons( e ) {
7666 e.preventDefault();
7667 installOrActivate( this, 'frm_multiple_addons' );
7668 }
7669
7670 function activateAddon( e ) {
7671 e.preventDefault();
7672 installOrActivate( this, 'frm_activate_addon' );
7673 }
7674
7675 function installAddon( e ) {
7676 e.preventDefault();
7677 installOrActivate( this, 'frm_install_addon' );
7678 }
7679
7680 function installOrActivate( clicked, action ) {
7681 var button, plugin, el, message;
7682
7683 // Remove any leftover error messages, output an icon and get the plugin basename that needs to be activated.
7684 jQuery( '.frm-addon-error' ).remove();
7685 button = jQuery( clicked );
7686 plugin = button.attr( 'rel' );
7687 el = button.parent();
7688 message = el.parent().find( '.addon-status-label' );
7689
7690 button.addClass( 'frm_loading_button' );
7691
7692 // Process the Ajax to perform the activation.
7693 jQuery.ajax({
7694 url: ajaxurl,
7695 type: 'POST',
7696 async: true,
7697 cache: false,
7698 dataType: 'json',
7699 data: {
7700 action: action,
7701 nonce: frmGlobal.nonce,
7702 plugin: plugin
7703 },
7704 success: function( response ) {
7705 var saveAndReload, error;
7706
7707 if ( 'string' !== typeof response && 'string' === typeof response.message ) {
7708 if ( 'undefined' !== typeof response.saveAndReload ) {
7709 saveAndReload = response.saveAndReload;
7710 }
7711 response = response.message;
7712 }
7713
7714 error = extractErrorFromAddOnResponse( response );
7715
7716 if ( error ) {
7717 addonError( error, el, button );
7718 return;
7719 }
7720
7721 afterAddonInstall( response, button, message, el, saveAndReload );
7722 },
7723 error: function() {
7724 button.removeClass( 'frm_loading_button' );
7725 }
7726 });
7727 }
7728
7729 function installAddonWithCreds( e ) {
7730 // Prevent the default action, let the user know we are attempting to install again and go with it.
7731 e.preventDefault();
7732
7733 // Now let's make another Ajax request once the user has submitted their credentials.
7734 var proceed = jQuery( this ),
7735 el = proceed.parent().parent(),
7736 plugin = proceed.attr( 'rel' );
7737
7738 proceed.addClass( 'frm_loading_button' );
7739
7740 jQuery.ajax({
7741 url: ajaxurl,
7742 type: 'POST',
7743 async: true,
7744 cache: false,
7745 dataType: 'json',
7746 data: {
7747 action: 'frm_install_addon',
7748 nonce: frm_admin_js.nonce,
7749 plugin: plugin,
7750 hostname: el.find( '#hostname' ).val(),
7751 username: el.find( '#username' ).val(),
7752 password: el.find( '#password' ).val()
7753 },
7754 success: function( response ) {
7755 var error = extractErrorFromAddOnResponse( response );
7756
7757 if ( error ) {
7758 addonError( error, el, proceed );
7759 return;
7760 }
7761
7762 afterAddonInstall( response, proceed, message, el );
7763 },
7764 error: function() {
7765 proceed.removeClass( 'frm_loading_button' );
7766 }
7767 });
7768 }
7769
7770 function afterAddonInstall( response, button, message, el, saveAndReload ) {
7771 var $addonStatus, refreshPage;
7772
7773 $addonStatus = jQuery( document.getElementById( 'frm-addon-status' ) );
7774 // The Ajax request was successful, so let's update the output.
7775 button.css({ opacity: '0' });
7776 message.text( frm_admin_js.active );
7777 jQuery( '#frm-oneclick' ).hide();
7778 $addonStatus.text( response ).show();
7779 jQuery( '#frm_upgrade_modal h2' ).hide();
7780 jQuery( '#frm_upgrade_modal .frm_lock_icon' ).addClass( 'frm_lock_open_icon' );
7781 jQuery( '#frm_upgrade_modal .frm_lock_icon use' ).attr( 'xlink:href', '#frm_lock_open_icon' );
7782
7783 // Proceed with CSS changes
7784 el.parent().removeClass( 'frm-addon-not-installed frm-addon-installed' ).addClass( 'frm-addon-active' );
7785 button.removeClass( 'frm_loading_button' );
7786
7787 // Maybe refresh import and SMTP pages
7788 refreshPage = document.querySelectorAll( '.frm-admin-page-import, #frm-admin-smtp, #frm-welcome' );
7789 if ( refreshPage.length > 0 ) {
7790 window.location.reload();
7791 } else if ( 'settings' === saveAndReload ) {
7792 $addonStatus.append( getSaveAndReloadSettingsOptions() );
7793 }
7794 }
7795
7796 function getSaveAndReloadSettingsOptions() {
7797 var wrapper = div({ id: 'frm_save_and_reload_options' });
7798 wrapper.appendChild( saveAndReloadSettingsButton() );
7799 wrapper.appendChild( closePopupButton() );
7800 return wrapper;
7801 }
7802
7803 function saveAndReloadSettingsButton() {
7804 var button = document.createElement( 'button' );
7805 button.id = 'frm_save_and_reload_settings';
7806 button.classList.add( 'button', 'button-primary', 'frm-button-primary' );
7807 button.textContent = __( 'Save and Reload', 'formidable' );
7808 return button;
7809 }
7810
7811 function closePopupButton() {
7812 var a = document.createElement( 'a' );
7813 a.setAttribute( 'href', '#' );
7814 a.classList.add( 'button', 'button-secondary', 'frm-button-secondary', 'dismiss' );
7815 a.textContent = __( 'Close', 'formidable' );
7816 return a;
7817 }
7818
7819 function extractErrorFromAddOnResponse( response ) {
7820 if ( typeof response !== 'string' ) {
7821 if ( typeof response.success !== 'undefined' && response.success ) {
7822 return false;
7823 }
7824
7825 if ( response.form ) {
7826 if ( jQuery( response.form ).is( '#message' ) ) {
7827 return {
7828 message: jQuery( response.form ).find( 'p' ).html()
7829 };
7830 }
7831 }
7832
7833 return response;
7834 }
7835
7836 return false;
7837 }
7838
7839 function addonError( response, el, button ) {
7840 if ( response.form ) {
7841 jQuery( '.frm-inline-error' ).remove();
7842 button.closest( '.frm-card' )
7843 .html( response.form )
7844 .css({ padding: 5 })
7845 .find( '#upgrade' )
7846 .attr( 'rel', button.attr( 'rel' ) )
7847 .on( 'click', installAddonWithCreds );
7848 } else {
7849 el.append( '<div class="frm-addon-error frm_error_style"><p><strong>' + response.message + '</strong></p></div>' );
7850 button.removeClass( 'frm_loading_button' );
7851 jQuery( '.frm-addon-error' ).delay( 4000 ).fadeOut();
7852 }
7853 }
7854
7855 /* Templates */
7856
7857 function initNewFormModal() {
7858 var installFormTrigger,
7859 activeHoverIcons,
7860 $modal,
7861 handleError,
7862 handleEmailAddressError,
7863 handleConfirmEmailAddressError,
7864 showFreeTemplatesForm,
7865 firstLockedTemplate,
7866 isShowFreeTemplatesFormFirst,
7867 url,
7868 urlParams;
7869
7870 url = new URL( window.location.href );
7871 urlParams = url.searchParams;
7872
7873 isShowFreeTemplatesFormFirst = urlParams.get( 'free-templates' );
7874
7875 jQuery( document ).on( 'click', '.frm-trigger-new-form-modal', triggerNewFormModal );
7876 $modal = initModal( '#frm_new_form_modal', '600px' );
7877
7878 installFormTrigger = document.createElement( 'a' );
7879 installFormTrigger.classList.add( 'frm-install-template', 'frm_hidden' );
7880 document.body.appendChild( installFormTrigger );
7881
7882 jQuery( '.frm-install-template' ).on( 'click', function( event ) {
7883 var $h3Clone = jQuery( this ).closest( 'li, td' ).find( 'h3' ).clone(),
7884 nameLabel = document.getElementById( 'frm_new_name' ),
7885 descLabel = document.getElementById( 'frm_new_desc' ),
7886 oldName;
7887
7888 $h3Clone.find( 'svg, .frm-plan-required-tag' ).remove();
7889 oldName = $h3Clone.html().trim();
7890
7891 event.preventDefault();
7892
7893 document.getElementById( 'frm_template_name' ).value = oldName;
7894 document.getElementById( 'frm_link' ).value = this.attributes.rel.value;
7895 document.getElementById( 'frm_action_type' ).value = 'frm_install_template';
7896 nameLabel.innerHTML = nameLabel.getAttribute( 'data-form' );
7897 descLabel.innerHTML = descLabel.getAttribute( 'data-form' );
7898 $modal.dialog( 'open' );
7899 });
7900
7901 jQuery( document ).on( 'submit', '#frm-new-template', installTemplate );
7902
7903 jQuery( document ).on( 'click', '.frm-hover-icons .frm-preview-form', function( event ) {
7904 var $li, link, iframe,
7905 container = document.getElementById( 'frm-preview-block' );
7906
7907 event.preventDefault();
7908
7909 $li = jQuery( this ).closest( 'li' );
7910 link = $li.attr( 'data-preview' );
7911
7912 if ( link.indexOf( ajaxurl ) > -1 ) {
7913 iframe = document.createElement( 'iframe' );
7914 iframe.src = link;
7915 iframe.height = '400';
7916 iframe.width = '100%';
7917 container.innerHTML = '';
7918 container.appendChild( iframe );
7919 } else {
7920 frmApiPreview( container, link );
7921 }
7922
7923 jQuery( '#frm-preview-title' ).text( getStrippedTemplateName( $li ) );
7924 $modal.attr( 'frm-page', 'preview' );
7925 activeHoverIcons = jQuery( this ).closest( '.frm-hover-icons' );
7926 });
7927
7928 jQuery( document ).on( 'click', 'li .frm-hover-icons .frm-create-form', function( event ) {
7929 var $li, name, link, action;
7930
7931 event.preventDefault();
7932
7933 $li = jQuery( this ).closest( 'li' );
7934
7935 if ( $li.is( '[data-href]' ) ) {
7936 window.location = $li.attr( 'data-href' );
7937 return;
7938 }
7939
7940 if ( $li.hasClass( 'frm-add-blank-form' ) ) {
7941 name = link = '';
7942 action = 'frm_install_form';
7943 } else if ( $li.is( '[data-rel]' ) ) {
7944 name = getStrippedTemplateName( $li );
7945 link = $li.attr( 'data-rel' );
7946 action = 'frm_install_template';
7947 } else {
7948 return;
7949 }
7950
7951 transitionToAddDetails( $modal, name, link, action );
7952 });
7953
7954 // Welcome page modals.
7955 jQuery( document ).on( 'click', '.frm-create-blank-form', function( event ) {
7956 event.preventDefault();
7957 jQuery( '.frm-trigger-new-form-modal' ).trigger( 'click' );
7958 transitionToAddDetails( $modal, '', '', 'frm_install_form' );
7959
7960 // Close the modal with the cancel button.
7961 jQuery( '.frm-modal-cancel.frm-back-to-all-templates' ).on( 'click', function() {
7962 jQuery( '.ui-widget-overlay' ).trigger( 'click' );
7963 });
7964 });
7965
7966 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 ) {
7967 var $hoverIcons, $trigger,
7968 $li = jQuery( this ).closest( 'li' ),
7969 triggerClass = $li.hasClass( 'frm-locked-template' ) ? 'frm-unlock-form' : 'frm-create-form';
7970
7971 $hoverIcons = $li.find( '.frm-hover-icons' );
7972 if ( ! $hoverIcons.length ) {
7973 $li.trigger( 'mouseover' );
7974 $hoverIcons = $li.find( '.frm-hover-icons' );
7975 $hoverIcons.hide();
7976 }
7977
7978 $trigger = $hoverIcons.find( '.' + triggerClass );
7979 $trigger.trigger( 'click' );
7980 });
7981
7982 jQuery( document ).on( 'click', 'li .frm-hover-icons .frm-delete-form', function( event ) {
7983 var $li,
7984 trigger;
7985
7986 event.preventDefault();
7987
7988 $li = jQuery( this ).closest( 'li' );
7989 $li.addClass( 'frm-deleting' );
7990 trigger = document.createElement( 'a' );
7991 trigger.setAttribute( 'href', '#' );
7992 trigger.setAttribute( 'data-id', $li.attr( 'data-formid' ) );
7993 $li.attr( 'id', 'frm-template-custom-' + $li.attr( 'data-formid' ) );
7994 jQuery( trigger ).on( 'click', trashTemplate );
7995 trigger.click();
7996 setTemplateCount( $li.closest( '.accordion-section' ).get( 0 ) );
7997 });
7998
7999 showFreeTemplatesForm = function( $el ) {
8000 var formContainer = document.getElementById( 'frmapi-email-form' );
8001 jQuery.ajax({
8002 dataType: 'json',
8003 url: formContainer.getAttribute( 'data-url' ),
8004 success: function( json ) {
8005 var form = json.renderedHtml;
8006 form = form.replace( /<script\b[^<]*(community.formidableforms.com\/wp-includes\/js\/jquery\/jquery)[^<]*><\/script>/gi, '' );
8007 form = form.replace( /<link\b[^>]*(formidableforms.css)[^>]*>/gi, '' );
8008 formContainer.innerHTML = form;
8009 }
8010 });
8011
8012 $modal.attr( 'frm-page', 'email' );
8013 $modal.attr( 'frm-this-form', $el.attr( 'data-key' ) );
8014 $el.append( installFormTrigger );
8015 };
8016
8017 jQuery( document ).on( 'click', 'li.frm-locked-template .frm-hover-icons .frm-unlock-form', function( event ) {
8018 var $li,
8019 activePage;
8020
8021 event.preventDefault();
8022
8023 $li = jQuery( this ).closest( '.frm-locked-template' );
8024
8025 if ( $li.hasClass( 'frm-free-template' ) ) {
8026 showFreeTemplatesForm( $li );
8027 return;
8028 }
8029
8030 if ( $modal.hasClass( 'frm-expired' ) ) {
8031 activePage = 'renew';
8032 } else {
8033 activePage = 'upgrade';
8034 }
8035
8036 $modal.attr( 'frm-page', activePage );
8037 });
8038
8039 jQuery( document ).on( 'click', '#frm_new_form_modal #frm-template-drop', function() {
8040 jQuery( this )
8041 .closest( '.accordion-section-content' ).css( 'overflow', 'visible' )
8042 .closest( '.accordion-section' ).css( 'z-index', 1 );
8043 });
8044
8045 jQuery( document ).on( 'click', '#frm_new_form_modal #frm-template-drop + .frm-dropdown-menu .frm-build-template', function() {
8046 var name = this.getAttribute( 'data-fullname' ),
8047 link = this.getAttribute( 'data-formid' ),
8048 action = 'frm_build_template';
8049 transitionToAddDetails( $modal, name, link, action );
8050 });
8051
8052 handleError = function( inputId, errorId, type, message ) {
8053 var $error = jQuery( errorId );
8054 $error.removeClass( 'frm_hidden' ).attr( 'frm-error', type );
8055
8056 if ( typeof message !== 'undefined' ) {
8057 $error.find( 'span[frm-error="' + type + '"]' ).text( message );
8058 }
8059
8060 jQuery( inputId ).one( 'keyup', function() {
8061 $error.addClass( 'frm_hidden' );
8062 });
8063 };
8064
8065 handleEmailAddressError = function( type ) {
8066 handleError( '#frm_leave_email', '#frm_leave_email_error', type );
8067 };
8068
8069 jQuery( document ).on( 'click', '#frm-add-my-email-address', function( event ) {
8070 var email = document.getElementById( 'frm_leave_email' ).value.trim(),
8071 regex,
8072 $hiddenForm,
8073 $hiddenEmailField;
8074
8075 event.preventDefault();
8076
8077 if ( '' === email ) {
8078 handleEmailAddressError( 'empty' );
8079 return;
8080 }
8081
8082 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;
8083
8084 if ( regex.test( email ) === false ) {
8085 handleEmailAddressError( 'invalid' );
8086 return;
8087 }
8088
8089 $hiddenForm = jQuery( '#frmapi-email-form' ).find( 'form' );
8090 $hiddenEmailField = $hiddenForm.find( '[type="email"]' ).not( '.frm_verify' );
8091 if ( ! $hiddenEmailField.length ) {
8092 return;
8093 }
8094
8095 $hiddenEmailField.val( email );
8096 jQuery.ajax({
8097 type: 'POST',
8098 url: $hiddenForm.attr( 'action' ),
8099 data: $hiddenForm.serialize() + '&action=frm_forms_preview'
8100 }).done( function( data ) {
8101 var message = jQuery( data ).find( '.frm_message' ).text().trim();
8102 if ( message.indexOf( 'Thanks!' ) >= 0 ) {
8103 $modal.attr( 'frm-page', 'code' );
8104 } else {
8105 handleEmailAddressError( 'invalid' );
8106 }
8107 });
8108 });
8109
8110 handleConfirmEmailAddressError = function( type, message ) {
8111 handleError( '#frm_code_from_email', '#frm_code_from_email_error', type, message );
8112 };
8113
8114 jQuery( document ).on( 'click', '.frm-confirm-email-address', function( event ) {
8115 var code = document.getElementById( 'frm_code_from_email' ).value.trim();
8116
8117 event.preventDefault();
8118
8119 if ( '' === code ) {
8120 handleConfirmEmailAddressError( 'empty' );
8121 return;
8122 }
8123
8124 jQuery.ajax({
8125 type: 'POST',
8126 url: ajaxurl,
8127 dataType: 'json',
8128 data: {
8129 action: 'template_api_signup',
8130 nonce: frmGlobal.nonce,
8131 code: code,
8132 key: $modal.attr( 'frm-this-form' )
8133 },
8134 success: function( response ) {
8135 if ( response.success ) {
8136 if ( isShowFreeTemplatesFormFirst ) {
8137 // Remove free-templates param from URL then reload page.
8138 urlParams.delete( 'free-templates' );
8139 url.search = urlParams.toString();
8140 window.location.href = url.toString();
8141
8142 return;
8143 }
8144
8145 if ( typeof response.data !== 'undefined' && typeof response.data.url !== 'undefined' ) {
8146 installFormTrigger.setAttribute( 'rel', response.data.url );
8147 installFormTrigger.click();
8148 $modal.attr( 'frm-page', 'details' );
8149
8150 const hookName = 'frm_new_form_modal_form';
8151 wp.hooks.doAction( hookName, $modal );
8152
8153 document.getElementById( 'frm_action_type' ).value = 'frm_install_template';
8154
8155 if ( typeof response.data.urlByKey !== 'undefined' ) {
8156 updateTemplateModalFreeUrls( response.data.urlByKey );
8157 }
8158 }
8159 } else {
8160 if ( Array.isArray( response.data ) && response.data.length ) {
8161 handleConfirmEmailAddressError( 'custom', response.data[0].message );
8162 } else {
8163 handleConfirmEmailAddressError( 'wrong-code' );
8164 }
8165
8166 jQuery( '#frm_code_from_email_options' ).removeClass( 'frm_hidden' );
8167 }
8168 }
8169 });
8170 });
8171
8172 jQuery( document ).on( 'click', '#frm-change-email-address', function() {
8173 $modal.attr( 'frm-page', 'email' );
8174 });
8175
8176 jQuery( document ).on( 'click', '#frm-resend-code', function() {
8177 document.getElementById( 'frm_code_from_email' ).value = '';
8178 jQuery( '#frm_code_from_email_options, #frm_code_from_email_error' ).addClass( 'frm_hidden' );
8179 document.getElementById( 'frm-add-my-email-address' ).click();
8180 });
8181
8182 jQuery( document ).on( 'frmAfterSearch', '#frm_new_form_modal #template-search-input', function() {
8183 var categories = $modal.get( 0 ).querySelector( '.frm-categories-list' ).children,
8184 categoryIndex,
8185 category,
8186 searchableTemplates,
8187 count;
8188
8189 for ( categoryIndex in categories ) {
8190 if ( isNaN( categoryIndex ) ) {
8191 continue;
8192 }
8193
8194 category = categories[ categoryIndex ];
8195 if ( ! category.classList.contains( 'accordion-section' ) ) {
8196 continue;
8197 }
8198
8199 searchableTemplates = category.querySelectorAll( '.frm-searchable-template:not(.frm_hidden)' );
8200 count = searchableTemplates.length;
8201 jQuery( category ).toggleClass( 'frm_hidden', this.value !== '' && ! count );
8202 setTemplateCount( category, searchableTemplates );
8203 }
8204 });
8205
8206 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 ) {
8207 document.getElementById( 'frm-create-title' ).removeAttribute( 'frm-type' );
8208 $modal.attr( 'frm-page', 'create' );
8209 });
8210
8211 jQuery( document ).on( 'click', '.frm-use-this-template', function( event ) {
8212 var $trigger;
8213
8214 event.preventDefault();
8215
8216 $trigger = activeHoverIcons.find( '.frm-create-form' );
8217 if ( $trigger.closest( '.frm-selectable' ).hasClass( 'frm-locked-template' ) ) {
8218 $trigger = activeHoverIcons.find( '.frm-unlock-form' );
8219 }
8220
8221 $trigger.trigger( 'click' );
8222 });
8223
8224 jQuery( document ).on( 'click', '.frm-submit-new-template', function( event ) {
8225 var button;
8226 event.preventDefault();
8227 button = document.getElementById( 'frm-new-template' ).querySelector( 'button' );
8228 if ( null !== button ) {
8229 button.click();
8230 }
8231 });
8232
8233 if ( urlParams.get( 'triggerNewFormModal' ) ) {
8234 triggerNewFormModal();
8235
8236 if ( isShowFreeTemplatesFormFirst ) {
8237 firstLockedTemplate = jQuery( 'li.frm-locked-template.frm-free-template' ).eq( 0 );
8238
8239 if ( firstLockedTemplate.length ) {
8240 showFreeTemplatesForm( firstLockedTemplate );
8241 }
8242 }
8243 }
8244 }
8245
8246 function updateTemplateModalFreeUrls( urlByKey ) {
8247 jQuery( '#frm_new_form_modal' ).find( '.frm-selectable[data-key]' ).each( function() {
8248 var $template = jQuery( this ),
8249 key = $template.attr( 'data-key' );
8250 if ( 'undefined' !== typeof urlByKey[ key ]) {
8251 $template.removeClass( 'frm-locked-template' );
8252 $template.find( 'h3 svg' ).remove(); // remove the lock from the title
8253 $template.attr( 'data-rel', urlByKey[ key ]);
8254 }
8255 });
8256 }
8257
8258 function transitionToAddDetails( $modal, name, link, action ) {
8259 var nameLabel = document.getElementById( 'frm_new_name' ),
8260 descLabel = document.getElementById( 'frm_new_desc' ),
8261 type = [ 'frm_install_template', 'frm_install_form' ].indexOf( action ) >= 0 ? 'form' : 'template',
8262 templateNameInput = document.getElementById( 'frm_template_name' );
8263
8264 templateNameInput.value = name;
8265 document.getElementById( 'frm_link' ).value = link;
8266 document.getElementById( 'frm_action_type' ).value = action;
8267 nameLabel.textContent = nameLabel.getAttribute( 'data-' + type );
8268 if ( descLabel !== null ) {
8269 descLabel.textContent = descLabel.getAttribute( 'data-' + type );
8270 }
8271
8272 document.getElementById( 'frm-create-title' ).setAttribute( 'frm-type', type );
8273
8274 $modal.attr( 'frm-page', 'details' );
8275
8276 const hookName = 'frm_new_form_modal_form';
8277 wp.hooks.doAction( hookName, $modal );
8278
8279 if ( '' === name ) {
8280 templateNameInput.focus();
8281 }
8282 }
8283
8284 function getStrippedTemplateName( $li ) {
8285 var $clone = $li.find( 'h3' ).clone();
8286 $clone.find( 'svg, .frm-plan-required-tag, .frm-new-pill' ).remove();
8287 return $clone.html().trim();
8288 }
8289
8290 function setTemplateCount( category, searchableTemplates ) {
8291 var count,
8292 templateIndex,
8293 availableCounter,
8294 availableCount;
8295
8296 if ( typeof searchableTemplates === 'undefined' ) {
8297 searchableTemplates = category.querySelectorAll( '.frm-searchable-template:not(.frm_hidden):not(.frm-deleting)' );
8298 }
8299
8300 count = searchableTemplates.length;
8301 category.querySelector( '.frm-template-count' ).textContent = count;
8302
8303 jQuery( category ).find( '.frm-templates-plural' ).toggleClass( 'frm_hidden', count === 1 );
8304 jQuery( category ).find( '.frm-templates-singular' ).toggleClass( 'frm_hidden', count !== 1 );
8305
8306 availableCounter = category.querySelector( '.frm-available-templates-count' );
8307 if ( availableCounter !== null ) {
8308 availableCount = 0;
8309 for ( templateIndex in searchableTemplates ) {
8310 if ( ! isNaN( templateIndex ) && ! searchableTemplates[ templateIndex ].classList.contains( 'frm-locked-template' ) ) {
8311 availableCount++;
8312 }
8313 }
8314
8315 availableCounter.textContent = availableCount;
8316 }
8317 }
8318
8319 function initSelectionAutocomplete() {
8320 frmDom.autocomplete.initSelectionAutocomplete();
8321 }
8322
8323 function nextInstallStep( thisStep ) {
8324 thisStep.classList.add( 'frm_grey' );
8325 thisStep.nextElementSibling.classList.remove( 'frm_grey' );
8326 }
8327
8328 function frmApiPreview( cont, link ) {
8329 cont.innerHTML = '<div class="frm-wait"></div>';
8330 jQuery.ajax({
8331 dataType: 'json',
8332 url: link,
8333 success: function( json ) {
8334 var form = json.renderedHtml;
8335 form = form.replace( /<script\b[^<]*(js\/jquery\/jquery)[^<]*><\/script>/gi, '' );
8336 form = form.replace( /<link\b[^>]*(jquery-ui.min.css)[^>]*>/gi, '' );
8337 form = form.replace( ' frm_logic_form ', ' ' );
8338 form = form.replace( '<form ', '<form onsubmit="event.preventDefault();" ' );
8339 cont.innerHTML = '<div class="frm-wait" id="frm-remove-me"></div><div class="frm-fade" id="frm-show-me">' +
8340 form + '</div>';
8341 setTimeout( function() {
8342 document.getElementById( 'frm-remove-me' ).style.display = 'none';
8343 document.getElementById( 'frm-show-me' ).style.opacity = '1';
8344 }, 300 );
8345 }
8346 });
8347 }
8348
8349 function installTemplateFieldset( e ) {
8350 /*jshint validthis:true */
8351 var fieldset = this.parentNode.parentNode,
8352 action = fieldset.elements.type.value,
8353 button = this;
8354 e.preventDefault();
8355 button.classList.add( 'frm_loading_button' );
8356 installNewForm( fieldset, action, button );
8357 }
8358
8359 function installTemplate( e ) {
8360 /*jshint validthis:true */
8361 var action = this.elements.type.value,
8362 button = this.querySelector( 'button' );
8363 e.preventDefault();
8364 button.classList.add( 'frm_loading_button' );
8365 installNewForm( this, action, button );
8366 }
8367
8368 function installNewForm( form, action, button ) {
8369 const formData = formToData( form );
8370 const formName = formData.template_name;
8371 const formDesc = formData.template_desc;
8372 const link = form.elements.link.value;
8373
8374 let data = {
8375 action: action,
8376 xml: link,
8377 name: formName,
8378 desc: formDesc,
8379 form: JSON.stringify( formData ),
8380 nonce: frmGlobal.nonce
8381 };
8382
8383 const hookName = 'frm_before_install_new_form';
8384 const filterArgs = { formData };
8385 data = wp.hooks.applyFilters( hookName, data, filterArgs );
8386
8387 postAjax( data, function( response ) {
8388 if ( typeof response.redirect !== 'undefined' ) {
8389 const redirect = response.redirect;
8390 if ( typeof form.elements.redirect === 'undefined' ) {
8391 window.location = redirect;
8392 } else {
8393 const href = document.getElementById( 'frm-redirect-link' );
8394 if ( typeof link !== 'undefined' && href !== null ) {
8395 // Show the next installation step.
8396 href.setAttribute( 'href', redirect );
8397 href.classList.remove( 'frm_grey', 'disabled' );
8398 nextInstallStep( form.parentNode.parentNode );
8399 button.classList.add( 'frm_grey', 'disabled' );
8400 }
8401 }
8402 } else {
8403 jQuery( '.spinner' ).css( 'visibility', 'hidden' );
8404
8405 // Show response.message
8406 if ( response.message && typeof form.elements.show_response !== 'undefined' ) {
8407 const showError = document.getElementById( form.elements.show_response.value );
8408 if ( showError !== null ) {
8409 showError.innerHTML = response.message;
8410 showError.classList.remove( 'frm_hidden' );
8411 }
8412 }
8413 }
8414 button.classList.remove( 'frm_loading_button' );
8415 });
8416 }
8417
8418 function handleCaptchaTypeChange( e ) {
8419 const thresholdContainer = document.getElementById( 'frm_captcha_threshold_container' );
8420 if ( thresholdContainer ) {
8421 thresholdContainer.classList.toggle( 'frm_hidden', 'v3' !== e.target.value );
8422 }
8423 }
8424
8425 function trashTemplate( e ) {
8426 /*jshint validthis:true */
8427 var id = this.getAttribute( 'data-id' );
8428 e.preventDefault();
8429
8430 data = {
8431 action: 'frm_forms_trash',
8432 id: id,
8433 nonce: frmGlobal.nonce
8434 };
8435 postAjax( data, function() {
8436 var card = document.getElementById( 'frm-template-custom-' + id );
8437 fadeOut( card, function() {
8438 card.parentNode.removeChild( card );
8439 });
8440 });
8441 }
8442
8443 function searchContent() {
8444 /*jshint validthis:true */
8445 var i,
8446 regEx = false,
8447 searchText = this.value.toLowerCase(),
8448 toSearch = this.getAttribute( 'data-tosearch' ),
8449 items = document.getElementsByClassName( toSearch );
8450
8451 if ( this.tagName === 'SELECT' ) {
8452 searchText = selectedOptions( this );
8453 searchText = searchText.join( '|' ).toLowerCase();
8454 regEx = true;
8455 }
8456
8457 if ( toSearch === 'frm-action' && searchText !== '' ) {
8458 var addons = document.getElementById( 'frm_email_addon_menu' ).classList;
8459 addons.remove( 'frm-all-actions' );
8460 addons.add( 'frm-limited-actions' );
8461 }
8462
8463 for ( i = 0; i < items.length; i++ ) {
8464 var innerText = items[i].innerText.toLowerCase();
8465 if ( searchText === '' ) {
8466 items[i].classList.remove( 'frm_hidden' );
8467 items[i].classList.remove( 'frm-search-result' );
8468 } else if ( ( regEx && new RegExp( searchText ).test( innerText ) ) || innerText.indexOf( searchText ) >= 0 ) {
8469 items[i].classList.remove( 'frm_hidden' );
8470 items[i].classList.add( 'frm-search-result' );
8471 } else {
8472 items[i].classList.add( 'frm_hidden' );
8473 items[i].classList.remove( 'frm-search-result' );
8474 }
8475 }
8476
8477 jQuery( this ).trigger( 'frmAfterSearch' );
8478 }
8479
8480 function stopPropagation( e ) {
8481 e.stopPropagation();
8482 }
8483
8484 /* Helpers */
8485
8486 function selectedOptions( select ) {
8487 var opt,
8488 result = [],
8489 options = select && select.options;
8490
8491 for ( var i = 0, iLen = options.length; i < iLen; i++ ) {
8492 opt = options[i];
8493
8494 if ( opt.selected ) {
8495 result.push( opt.value );
8496 }
8497 }
8498 return result;
8499 }
8500
8501 function triggerEvent( element, event ) {
8502 var evt = document.createEvent( 'HTMLEvents' );
8503 evt.initEvent( event, false, true );
8504 element.dispatchEvent( evt );
8505 }
8506
8507 function postAjax( data, success ) {
8508 let response;
8509
8510 const xmlHttp = new XMLHttpRequest();
8511 const params = typeof data === 'string' ? data : Object.keys( data ).map(
8512 function( k ) {
8513 return encodeURIComponent( k ) + '=' + encodeURIComponent( data[k]);
8514 }
8515 ).join( '&' );
8516
8517 xmlHttp.open( 'post', ajaxurl, true );
8518 xmlHttp.onreadystatechange = function() {
8519 if ( xmlHttp.readyState > 3 && xmlHttp.status == 200 ) {
8520 response = xmlHttp.responseText;
8521 try {
8522 response = JSON.parse( response );
8523 } catch ( e ) {
8524 // The response may not be JSON, so just return it.
8525 }
8526 success( response );
8527 }
8528 };
8529 xmlHttp.setRequestHeader( 'X-Requested-With', 'XMLHttpRequest' );
8530 xmlHttp.setRequestHeader( 'Content-type', 'application/x-www-form-urlencoded' );
8531 xmlHttp.send( params );
8532 return xmlHttp;
8533 }
8534
8535 function fadeOut( element, success ) {
8536 element.classList.add( 'frm-fade' );
8537 setTimeout( success, 1000 );
8538 }
8539
8540 function invisible( classes ) {
8541 jQuery( classes ).css( 'visibility', 'hidden' );
8542 }
8543
8544 function visible( classes ) {
8545 jQuery( classes ).css( 'visibility', 'visible' );
8546 }
8547
8548 function initModal( id, width ) {
8549 const $info = jQuery( id );
8550 if ( ! $info.length ) {
8551 return false;
8552 }
8553
8554 if ( typeof width === 'undefined' ) {
8555 width = '550px';
8556 }
8557
8558 const dialogArgs = {
8559 dialogClass: 'frm-dialog',
8560 modal: true,
8561 autoOpen: false,
8562 closeOnEscape: true,
8563 width: width,
8564 resizable: false,
8565 draggable: false,
8566 open: function() {
8567 jQuery( '.ui-dialog-titlebar' ).addClass( 'frm_hidden' ).removeClass( 'ui-helper-clearfix' );
8568 jQuery( '#wpwrap' ).addClass( 'frm_overlay' );
8569 jQuery( '.frm-dialog' ).removeClass( 'ui-widget ui-widget-content ui-corner-all' );
8570 $info.removeClass( 'ui-dialog-content ui-widget-content' );
8571 bindClickForDialogClose( $info );
8572 },
8573 close: function() {
8574 jQuery( '#wpwrap' ).removeClass( 'frm_overlay' );
8575 jQuery( '.spinner' ).css( 'visibility', 'hidden' );
8576
8577 this.removeAttribute( 'data-option-type' );
8578 const optionType = document.getElementById( 'bulk-option-type' );
8579 if ( optionType ) {
8580 optionType.value = '';
8581 }
8582 }
8583 };
8584
8585 $info.dialog( dialogArgs );
8586
8587 return $info;
8588 }
8589
8590 function toggle( cname, id ) {
8591 if ( id === '#' ) {
8592 var cont = document.getElementById( cname );
8593 var hidden = cont.style.display;
8594 if ( hidden === 'none' ) {
8595 cont.style.display = 'block';
8596 } else {
8597 cont.style.display = 'none';
8598 }
8599 } else {
8600 var vis = cname.is( ':visible' );
8601 if ( vis ) {
8602 cname.hide();
8603 } else {
8604 cname.show();
8605 }
8606 }
8607 }
8608
8609 function removeWPUnload() {
8610 window.onbeforeunload = null;
8611 var w = jQuery( window );
8612 w.off( 'beforeunload.widgets' );
8613 w.off( 'beforeunload.edit-post' );
8614 }
8615
8616 function addMultiselectLabelListener() {
8617 const clickListener = ( e ) => {
8618 if ( 'LABEL' !== e.target.nodeName ) {
8619 return;
8620 }
8621
8622 const labelFor = e.target.getAttribute( 'for' );
8623 if ( ! labelFor ) {
8624 return;
8625 }
8626
8627 const input = document.getElementById( labelFor );
8628 if ( ! input || ! input.nextElementSibling ) {
8629 return;
8630 }
8631
8632 const buttonToggle = input.nextElementSibling.querySelector( 'button.dropdown-toggle.multiselect' );
8633 if ( ! buttonToggle ) {
8634 return;
8635 }
8636
8637 const triggerMultiselectClick = () => buttonToggle.click();
8638 setTimeout( triggerMultiselectClick, 0 );
8639 };
8640 document.addEventListener( 'click', clickListener );
8641 }
8642
8643 function maybeChangeEmbedFormMsg() {
8644 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
8645 var fieldItem = document.getElementById( 'frm_field_id_' + fieldId );
8646 if ( null === fieldItem || 'form' !== fieldItem.dataset.type ) {
8647 return;
8648 }
8649
8650 fieldItem = jQuery( fieldItem );
8651
8652 if ( this.options[ this.selectedIndex ].value ) {
8653 fieldItem.find( '.frm-not-set' )[0].classList.add( 'frm_hidden' );
8654 var embedMsg = fieldItem.find( '.frm-embed-message' );
8655 embedMsg.html( embedMsg.data( 'embedmsg' ) + this.options[ this.selectedIndex ].text );
8656 fieldItem.find( '.frm-embed-field-placeholder' )[0].classList.remove( 'frm_hidden' );
8657 } else {
8658 fieldItem.find( '.frm-not-set' )[0].classList.remove( 'frm_hidden' );
8659 fieldItem.find( '.frm-embed-field-placeholder' )[0].classList.add( 'frm_hidden' );
8660 }
8661 }
8662
8663 function toggleProductType() {
8664 var settings = jQuery( this ).closest( '.frm-single-settings' ),
8665 container = settings.find( '.frmjs_product_choices' ),
8666 heading = settings.find( '.frm_prod_options_heading' ),
8667 currentVal = this.options[ this.selectedIndex ].value;
8668
8669 container.removeClass( 'frm_prod_type_single frm_prod_type_user_def' );
8670 heading.removeClass( 'frm_prod_user_def' );
8671
8672 if ( 'single' === currentVal ) {
8673 container.addClass( 'frm_prod_type_single' );
8674 } else if ( 'user_def' === currentVal ) {
8675 container.addClass( 'frm_prod_type_user_def' );
8676 heading.addClass( 'frm_prod_user_def' );
8677 }
8678 }
8679
8680 function isProductField( fieldId ) {
8681 var field = document.getElementById( 'frm_field_id_' + fieldId );
8682 if ( field === null ) {
8683 return false;
8684 } else {
8685 return 'product' === field.getAttribute( 'data-type' );
8686 }
8687 }
8688
8689 /**
8690 * Serialize form data with vanilla JS.
8691 */
8692 function formToData( form ) {
8693 var subKey, i,
8694 object = {},
8695 formData = form.elements;
8696
8697 for ( i = 0; i < formData.length; i++ ) {
8698 var input = formData[i],
8699 key = input.name,
8700 value = input.value,
8701 names = key.match( /(.*)\[(.*)\]/ );
8702
8703 if ( ( input.type === 'radio' || input.type === 'checkbox' ) && ! input.checked ) {
8704 continue;
8705 }
8706
8707 if ( names !== null ) {
8708 key = names[1];
8709 subKey = names[2];
8710 if ( ! Reflect.has( object, key ) ) {
8711 object[key] = {};
8712 }
8713 object[key][subKey] = value;
8714 continue;
8715 }
8716
8717 // Reflect.has in favor of: object.hasOwnProperty(key)
8718 if ( ! Reflect.has( object, key ) ) {
8719 object[key] = value;
8720 continue;
8721 }
8722 if ( ! Array.isArray( object[key]) ) {
8723 object[key] = [ object[key] ];
8724 }
8725 object[key].push( value );
8726 }
8727
8728 return object;
8729 }
8730
8731 /**
8732 * Show, hide, and sort subfields of Name field on form builder.
8733 *
8734 * @since 4.11
8735 */
8736 function handleNameFieldOnFormBuilder() {
8737 /**
8738 * Gets subfield element from cache.
8739 *
8740 * @param {String} fieldId Field ID.
8741 * @param {String} key Cache key.
8742 * @returns {HTMLElement|undefined} Return the element from cache or undefined if not found.
8743 */
8744 const getSubFieldElFromCache = ( fieldId, key ) => {
8745 window.frmCachedSubFields = window.frmCachedSubFields || {};
8746 window.frmCachedSubFields[fieldId] = window.frmCachedSubFields[fieldId] || {};
8747 return window.frmCachedSubFields[fieldId][key];
8748 };
8749
8750 /**
8751 * Sets subfield element to cache.
8752 *
8753 * @param {String} fieldId Field ID.
8754 * @param {String} key Cache key.
8755 * @param {HTMLElement} el Element.
8756 */
8757 const setSubFieldElToCache = ( fieldId, key, el ) => {
8758 window.frmCachedSubFields = window.frmCachedSubFields || {};
8759 window.frmCachedSubFields[fieldId] = window.frmCachedSubFields[fieldId] || {};
8760 window.frmCachedSubFields[fieldId][key] = el;
8761 };
8762
8763 /**
8764 * Gets column class from the number of columns.
8765 *
8766 * @param {Number} colCount Number of columns.
8767 * @returns {string}
8768 */
8769 const getColClass = colCount => 'frm' + parseInt( 12 / colCount );
8770
8771 const colClasses = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ].map( num => 'frm' + num );
8772
8773 const allSubFieldNames = [ 'first', 'middle', 'last' ];
8774
8775 /**
8776 * Handles name layout change.
8777 *
8778 * @param {Event} event Event object.
8779 */
8780 const onChangeLayout = event => {
8781 const value = event.target.value;
8782 const subFieldNames = value.split( '_' );
8783 const fieldId = event.target.dataset.fieldId;
8784
8785 /*
8786 * Live update form on the form builder.
8787 */
8788 const container = document.querySelector( '#field_' + fieldId + '_inner_container .frm_combo_inputs_container' );
8789 const newColClass = getColClass( subFieldNames.length );
8790
8791 // Set all sub field elements to cache and hide all of them first.
8792 allSubFieldNames.forEach( name => {
8793 const subFieldEl = container.querySelector( '[data-sub-field-name="' + name + '"]' );
8794 if ( subFieldEl ) {
8795 subFieldEl.classList.add( 'frm_hidden' );
8796 subFieldEl.classList.remove( ...colClasses );
8797 setSubFieldElToCache( fieldId, name, subFieldEl );
8798 }
8799 });
8800
8801 subFieldNames.forEach( subFieldName => {
8802 const subFieldEl = getSubFieldElFromCache( fieldId, subFieldName );
8803 if ( ! subFieldEl ) {
8804 return;
8805 }
8806
8807 subFieldEl.classList.remove( 'frm_hidden' );
8808 subFieldEl.classList.add( newColClass );
8809
8810 container.append( subFieldEl );
8811 });
8812
8813 /*
8814 * Live update subfield options.
8815 */
8816 // Hide all subfield options.
8817 allSubFieldNames.forEach( name => {
8818 const optionsEl = document.querySelector( '.frm_sub_field_options-' + name + '[data-field-id="' + fieldId + '"]' );
8819 if ( optionsEl ) {
8820 optionsEl.classList.add( 'frm_hidden' );
8821 setSubFieldElToCache( fieldId, name + '_options', optionsEl );
8822 }
8823 });
8824
8825 subFieldNames.forEach( subFieldName => {
8826 const optionsEl = getSubFieldElFromCache( fieldId, subFieldName + '_options' );
8827 if ( ! optionsEl ) {
8828 return;
8829 }
8830 optionsEl.classList.remove( 'frm_hidden' );
8831 });
8832 };
8833
8834 const dropdownSelector = '.frm_name_layout_dropdown';
8835 document.addEventListener( 'change', event => {
8836 if ( event.target.matches( dropdownSelector ) ) {
8837 onChangeLayout( event );
8838 }
8839 }, false );
8840 }
8841
8842 function debounce( func, wait = 100 ) {
8843 return frmDom.util.debounce( func, wait );
8844 }
8845
8846 return {
8847 init: function() {
8848 s = {};
8849
8850 // Bootstrap dropdown button
8851 jQuery( '.wp-admin' ).on( 'click', function( e ) {
8852 var t = jQuery( e.target );
8853 var $openDrop = jQuery( '.dropdown.open' );
8854 if ( $openDrop.length && ! t.hasClass( 'dropdown' ) && ! t.closest( '.dropdown' ).length ) {
8855 $openDrop.removeClass( 'open' );
8856 }
8857 });
8858 jQuery( '#frm_bs_dropdown:not(.open) a' ).on( 'click', focusSearchBox );
8859
8860 if ( typeof thisFormId === 'undefined' ) {
8861 thisFormId = jQuery( document.getElementById( 'form_id' ) ).val();
8862 }
8863
8864 frmAdminBuild.inboxBannerInit();
8865
8866 if ( $newFields.length > 0 ) {
8867 // only load this on the form builder page
8868 frmAdminBuild.buildInit();
8869 } else if ( document.getElementById( 'frm_notification_settings' ) !== null ) {
8870 // only load on form settings page
8871 frmAdminBuild.settingsInit();
8872 } else if ( document.getElementById( 'frm_styling_form' ) !== null ) {
8873 // load styling settings js
8874 frmAdminBuild.styleInit();
8875 } else if ( document.getElementById( 'form_global_settings' ) !== null ) {
8876 // global settings page
8877 frmAdminBuild.globalSettingsInit();
8878 } else if ( document.getElementById( 'frm_export_xml' ) !== null ) {
8879 // import/export page
8880 frmAdminBuild.exportInit();
8881 } else if ( document.getElementById( 'frm_dyncontent' ) !== null ) {
8882 // only load on views settings page
8883 frmAdminBuild.viewInit();
8884 } else if ( document.getElementById( 'frm_inbox_page' ) !== null ) {
8885 // Inbox page
8886 frmAdminBuild.inboxInit();
8887 } else if ( document.getElementById( 'frm-welcome' ) !== null ) {
8888 // Solution install page
8889 frmAdminBuild.solutionInit();
8890 } else {
8891 // New form selection page
8892 initNewFormModal();
8893 initSelectionAutocomplete();
8894
8895 jQuery( '[data-frmprint]' ).on( 'click', function() {
8896 window.print();
8897 return false;
8898 });
8899 }
8900
8901 var $advInfo = jQuery( document.getElementById( 'frm_adv_info' ) );
8902 if ( $advInfo.length > 0 || jQuery( '.frm_field_list' ).length > 0 ) {
8903 // only load on the form, form settings, and view settings pages
8904 frmAdminBuild.panelInit();
8905 }
8906
8907 loadTooltips();
8908 initUpgradeModal();
8909
8910 // used on build, form settings, and view settings
8911 var $shortCodeDiv = jQuery( document.getElementById( 'frm_shortcodediv' ) );
8912 if ( $shortCodeDiv.length > 0 ) {
8913 jQuery( 'a.edit-frm_shortcode' ).on( 'click', function() {
8914 if ( $shortCodeDiv.is( ':hidden' ) ) {
8915 $shortCodeDiv.slideDown( 'fast' );
8916 this.style.display = 'none';
8917 }
8918 return false;
8919 });
8920
8921 jQuery( '.cancel-frm_shortcode', '#frm_shortcodediv' ).on( 'click', function() {
8922 $shortCodeDiv.slideUp( 'fast' );
8923 $shortCodeDiv.siblings( 'a.edit-frm_shortcode' ).show();
8924 return false;
8925 });
8926 }
8927
8928 // tabs
8929 jQuery( document ).on( 'click', '#frm-nav-tabs a', clickNewTab );
8930 jQuery( '.post-type-frm_display .frm-nav-tabs a, .frm-category-tabs a' ).on( 'click', function() {
8931 if ( ! this.classList.contains( 'frm_noallow' ) ) {
8932 clickTab( this );
8933 return false;
8934 }
8935 });
8936 clickTab( jQuery( '.starttab a' ), 'auto' );
8937
8938 // submit the search form with dropdown
8939 jQuery( document ).on( 'click', '#frm-fid-search-menu a', function() {
8940 var val = this.id.replace( 'fid-', '' );
8941 jQuery( 'select[name="fid"]' ).val( val );
8942 triggerSubmit( document.getElementById( 'posts-filter' ) );
8943 return false;
8944 });
8945
8946 jQuery( '.frm_select_box' ).on( 'click focus', function() {
8947 this.select();
8948 });
8949
8950 jQuery( document ).on( 'input search change', '.frm-auto-search', searchContent );
8951 jQuery( document ).on( 'focusin click', '.frm-auto-search', stopPropagation );
8952 var autoSearch = jQuery( '.frm-auto-search' );
8953 if ( autoSearch.val() !== '' ) {
8954 autoSearch.trigger( 'keyup' );
8955 }
8956
8957 // Initialize Formidable Connection.
8958 FrmFormsConnect.init();
8959
8960 jQuery( document ).on( 'click', '.frm-install-addon', installAddon );
8961 jQuery( document ).on( 'click', '.frm-activate-addon', activateAddon );
8962 jQuery( document ).on( 'click', '.frm-solution-multiple', installMultipleAddons );
8963
8964 // prevent annoying confirmation message from WordPress
8965 jQuery( 'button, input[type=submit]' ).on( 'click', removeWPUnload );
8966
8967 addMultiselectLabelListener();
8968
8969 frmAdminBuild.hooks.addFilter(
8970 'frm_before_embed_modal',
8971 ( ids, { element, type }) => {
8972 if ( 'form' !== type ) {
8973 return ids;
8974 }
8975
8976 let formId, formKey;
8977 const row = element.closest( 'tr' );
8978
8979 if ( row ) {
8980 // Embed icon on form index.
8981 formId = parseInt( row.querySelector( '.column-id' ).textContent );
8982 formKey = row.querySelector( '.column-form_key' ).textContent;
8983 } else {
8984 // Embed button in form builder / form settings.
8985 formId = document.getElementById( 'form_id' ).value;
8986
8987 const formKeyInput = document.getElementById( 'frm_form_key' );
8988 if ( formKeyInput ) {
8989 formKey = formKeyInput.value;
8990 } else {
8991 const previewDrop = document.getElementById( 'frm-previewDrop' );
8992 if ( previewDrop ) {
8993 formKey = previewDrop.nextElementSibling.querySelector( '.dropdown-item a' ).getAttribute( 'href' ).split( 'form=' )[1];
8994 }
8995 }
8996 }
8997
8998 return [ formId, formKey ];
8999 }
9000 );
9001 },
9002
9003 buildInit: function() {
9004 var loadFieldId, $builderForm, builderArea;
9005
9006 if ( jQuery( '.frm_field_loading' ).length ) {
9007 loadFieldId = jQuery( '.frm_field_loading' ).first().attr( 'id' );
9008 loadFields( loadFieldId );
9009 }
9010
9011 setupSortable( 'ul.frm_sorting' );
9012
9013 jQuery( '.field_type_list > li:not(.frm_noallow)' ).draggable({
9014 connectToSortable: '#frm-show-fields',
9015 helper: 'clone',
9016 revert: 'invalid',
9017 delay: 10,
9018 cancel: '.frm-dropdown-menu'
9019 });
9020 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();
9021
9022 jQuery( '.frm_submit_ajax' ).on( 'click', submitBuild );
9023 jQuery( '.frm_submit_no_ajax' ).on( 'click', submitNoAjax );
9024
9025 jQuery( 'a.edit-form-status' ).on( 'click', slideDown );
9026 jQuery( '.cancel-form-status' ).on( 'click', slideUp );
9027 jQuery( '.save-form-status' ).on( 'click', function() {
9028 var newStatus = jQuery( document.getElementById( 'form_change_status' ) ).val();
9029 jQuery( 'input[name="new_status"]' ).val( newStatus );
9030 jQuery( document.getElementById( 'form-status-display' ) ).html( newStatus );
9031 jQuery( '.cancel-form-status' ).trigger( 'click' );
9032 return false;
9033 });
9034
9035 jQuery( '.frm_form_builder form' ).first().on( 'submit', function() {
9036 jQuery( '.inplace_field' ).trigger( 'blur' );
9037 });
9038
9039 initiateMultiselect();
9040 renumberPageBreaks();
9041
9042 $builderForm = jQuery( builderForm );
9043 builderArea = document.getElementById( 'frm_form_editor_container' );
9044 $builderForm.on( 'click', '.frm_add_logic_row', addFieldLogicRow );
9045 $builderForm.on( 'click', '.frm_add_watch_lookup_row', addWatchLookupRow );
9046 $builderForm.on( 'change', '.frm_get_values_form', updateGetValueFieldSelection );
9047 $builderForm.on( 'change', '.frm_logic_field_opts', getFieldValues );
9048 $builderForm.on( 'change', '.scale_maxnum, .scale_minnum', setScaleValues );
9049 $builderForm.on( 'change', '.radio_maxnum', setStarValues );
9050 $builderForm.on( 'frm-multiselect-changed', 'select[name^="field_options[admin_only_"]', adjustVisibilityValuesForEveryoneValues );
9051
9052 jQuery( document.getElementById( 'frm-insert-fields' ) ).on( 'click', '.frm_add_field', addFieldClick );
9053 $newFields.on( 'click', '.frm_clone_field', duplicateField );
9054 $builderForm.on( 'blur', 'input[id^="frm_calc"]', checkCalculationCreatedByUser );
9055 $builderForm.on( 'change', 'input.frm_format_opt', toggleInvalidMsg );
9056 $builderForm.on( 'change click', '[data-changeme]', liveChanges );
9057 $builderForm.on( 'click', 'input.frm_req_field', markRequired );
9058 $builderForm.on( 'click', '.frm_mark_unique', markUnique );
9059
9060 $builderForm.on( 'change', '.frm_repeat_format', toggleRepeatButtons );
9061 $builderForm.on( 'change', '.frm_repeat_limit', checkRepeatLimit );
9062 $builderForm.on( 'change', '.frm_js_checkbox_limit', checkCheckboxSelectionsLimit );
9063 $builderForm.on( 'input', 'input[name^="field_options[add_label_"]', function() {
9064 updateRepeatText( this, 'add' );
9065 });
9066 $builderForm.on( 'input', 'input[name^="field_options[remove_label_"]', function() {
9067 updateRepeatText( this, 'remove' );
9068 });
9069 $builderForm.on( 'change', 'select[name^="field_options[data_type_"]', maybeClearWatchFields );
9070 jQuery( builderArea ).on( 'click', '.frm-collapse-page', maybeCollapsePage );
9071 jQuery( builderArea ).on( 'click', '.frm-collapse-section', maybeCollapseSection );
9072 $builderForm.on( 'click', '.frm-single-settings h3', maybeCollapseSettings );
9073
9074 $builderForm.on( 'click', '.frm_toggle_sep_values', toggleSepValues );
9075 $builderForm.on( 'click', '.frm_toggle_image_options', toggleImageOptions );
9076 $builderForm.on( 'click', '.frm_remove_image_option', removeImageFromOption );
9077 $builderForm.on( 'click', '.frm_choose_image_box', addImageToOption );
9078 $builderForm.on( 'change', '.frm_hide_image_text', refreshOptionDisplay );
9079 $builderForm.on( 'change', '.frm_field_options_image_size', setImageSize );
9080 $builderForm.on( 'click', '.frm_multiselect_opt', toggleMultiselect );
9081 $newFields.on( 'mousedown', 'input, textarea, select', stopFieldFocus );
9082 $newFields.on( 'click', 'input[type=radio], input[type=checkbox]', stopFieldFocus );
9083 $newFields.on( 'click', '.frm_delete_field', clickDeleteField );
9084 $newFields.on( 'click', '.frm_select_field', clickSelectField );
9085 jQuery( document ).on( 'click', '.frm_delete_field_group', clickDeleteFieldGroup );
9086 jQuery( document ).on( 'click', '.frm_clone_field_group', duplicateFieldGroup );
9087 jQuery( document ).on( 'click', '#frm_field_group_controls > span:first-child', clickFieldGroupLayout );
9088 jQuery( document ).on( 'click', '.frm-row-layout-option', handleFieldGroupLayoutOptionClick );
9089 jQuery( document ).on( 'click', '.frm-merge-fields-into-row .frm-row-layout-option', handleFieldGroupLayoutOptionInsideMergeClick );
9090 jQuery( document ).on( 'click', '.frm-custom-field-group-layout', customFieldGroupLayoutClick );
9091 jQuery( document ).on( 'click', '.frm-merge-fields-into-row .frm-custom-field-group-layout', customFieldGroupLayoutInsideMergeClick );
9092 jQuery( document ).on( 'click', '.frm-break-field-group', breakFieldGroupClick );
9093 $newFields.on( 'click', '#frm_field_group_popup .frm_grid_container input', focusFieldGroupInputOnClick );
9094 jQuery( document ).on( 'click', '.frm-cancel-custom-field-group-layout', cancelCustomFieldGroupClick );
9095 jQuery( document ).on( 'click', '.frm-save-custom-field-group-layout', saveCustomFieldGroupClick );
9096 $newFields.on( 'click', 'ul.frm_sorting', fieldGroupClick );
9097 jQuery( document ).on( 'click', '.frm-merge-fields-into-row', mergeFieldsIntoRowClick );
9098 jQuery( document ).on( 'click', '.frm-delete-field-groups', deleteFieldGroupsClick );
9099 $newFields.on( 'click', '.frm-field-action-icons [data-toggle="dropdown"]', function() {
9100 this.closest( 'li.form-field' ).classList.add( 'frm-field-settings-open' );
9101 jQuery( document ).on( 'click', '#frm_builder_page', handleClickOutsideOfFieldSettings );
9102 });
9103 $newFields.on( 'mousemove', 'ul.frm_sorting', checkForMultiselectKeysOnMouseMove );
9104 $newFields.on( 'show.bs.dropdown', '.frm-field-action-icons', onFieldActionDropdownShow );
9105 jQuery( document ).on( 'show.bs.dropdown', '#frm_field_group_controls', onFieldGroupActionDropdownShow );
9106 $builderForm.on( 'click', '.frm_single_option a[data-removeid]', deleteFieldOption );
9107 $builderForm.on( 'mousedown', '.frm_single_option input[type=radio]', maybeUncheckRadio );
9108 $builderForm.on( 'focusin', '.frm_single_option input[type=text]', maybeClearOptText );
9109 $builderForm.on( 'click', '.frm_add_opt', addFieldOption );
9110 $builderForm.on( 'change', '.frm_single_option input', resetOptOnChange );
9111 $builderForm.on( 'change', '.frm_image_id', resetOptOnChange );
9112 $builderForm.on( 'change', '.frm_toggle_mult_sel', toggleMultSel );
9113 $builderForm.on( 'focusin', '.frm_classes', showBuilderModal );
9114
9115 $newFields.on( 'click', '.frm_primary_label', clickLabel );
9116 $newFields.on( 'click', '.frm_description', clickDescription );
9117 $newFields.on( 'click', 'li.ui-state-default', clickVis );
9118 $newFields.on( 'dblclick', 'li.ui-state-default', openAdvanced );
9119 $builderForm.on( 'change', '.frm_tax_form_select', toggleFormTax );
9120 $builderForm.on( 'change', 'select.conf_field', addConf );
9121
9122 $builderForm.on( 'change', '.frm_get_field_selection', getFieldSelection );
9123
9124 $builderForm.on( 'click', '.frm-show-inline-modal', maybeShowInlineModal );
9125
9126 $builderForm.on( 'click', '.frm-inline-modal .dismiss', dismissInlineModal );
9127 jQuery( document ).on( 'change', '[data-frmchange]', changeInputtedValue );
9128
9129 $builderForm.on( 'change', '.frm_include_extras_field', rePopCalcFieldsForSummary );
9130 $builderForm.on( 'change', 'select[name^="field_options[form_select_"]', maybeChangeEmbedFormMsg );
9131
9132 jQuery( document ).on( 'submit', '#frm_js_build_form', buildSubmittedNoAjax );
9133 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 );
9134
9135 popAllProductFields();
9136
9137 jQuery( document ).on( 'change', '.frmjs_prod_data_type_opt', toggleProductType );
9138
9139 jQuery( document ).on( 'focus', '.frm-single-settings ul input[type="text"][name^="field_options[options_"]', onOptionTextFocus );
9140 jQuery( document ).on( 'blur', '.frm-single-settings ul input[type="text"][name^="field_options[options_"]', onOptionTextBlur );
9141
9142 initBulkOptionsOverlay();
9143 hideEmptyEle();
9144 maybeDisableAddSummaryBtn();
9145 maybeHideQuantityProductFieldOption();
9146 handleNameFieldOnFormBuilder();
9147 toggleSectionHolder();
9148 },
9149
9150 settingsInit: function() {
9151 var formSettings, $loggedIn, $cookieExp, $editable,
9152 $formActions = jQuery( document.getElementById( 'frm_notification_settings' ) );
9153 //BCC, CC, and Reply To button functionality
9154 $formActions.on( 'click', '.frm_email_buttons', showEmailRow );
9155 $formActions.on( 'click', '.frm_remove_field', hideEmailRow );
9156 $formActions.on( 'change', '.frm_to_row, .frm_from_row', showEmailWarning );
9157 $formActions.on( 'change', '.frm_tax_selector', changePosttaxRow );
9158 $formActions.on( 'change', 'select.frm_single_post_field', checkDupPost );
9159 $formActions.on( 'change', 'select.frm_toggle_post_content', togglePostContent );
9160 $formActions.on( 'change', 'select.frm_dyncontent_opt', fillDyncontent );
9161 $formActions.on( 'change', '.frm_post_type', switchPostType );
9162 $formActions.on( 'click', '.frm_add_postmeta_row', addPostmetaRow );
9163 $formActions.on( 'click', '.frm_add_posttax_row', addPosttaxRow );
9164 $formActions.on( 'click', '.frm_toggle_cf_opts', toggleCfOpts );
9165 $formActions.on( 'click', '.frm_duplicate_form_action', copyFormAction );
9166 jQuery( 'select[data-toggleclass], input[data-toggleclass]' ).on( 'change', toggleFormOpts );
9167 jQuery( '.frm_actions_list' ).on( 'click', '.frm_active_action', addFormAction );
9168 jQuery( '#frm-show-groups, #frm-hide-groups' ).on( 'click', toggleActionGroups );
9169 initiateMultiselect();
9170
9171 //set actions icons to inactive
9172 jQuery( 'ul.frm_actions_list li' ).each( function() {
9173 checkActiveAction( jQuery( this ).children( 'a' ).data( 'actiontype' ) );
9174
9175 // If the icon is a background image, don't add BG color.
9176 var icon = jQuery( this ).find( 'i' );
9177 if ( icon.css( 'background-image' ) !== 'none' ) {
9178 icon.addClass( 'frm-inverse' );
9179 }
9180 });
9181
9182 jQuery( '.frm_submit_settings_btn' ).on( 'click', submitSettings );
9183
9184 formSettings = jQuery( '.frm_form_settings' );
9185 formSettings.on( 'click', '.frm_add_form_logic', addFormLogicRow );
9186 formSettings.on( 'blur', '.frm_email_blur', formatEmailSetting );
9187 formSettings.on( 'click', '.frm_already_used', onlyOneActionMessage );
9188
9189 formSettings.on( 'change', '#logic_link_submit', toggleSubmitLogic );
9190 formSettings.on( 'click', '.frm_add_submit_logic', addSubmitLogic );
9191 formSettings.on( 'change', '.frm_submit_logic_field_opts', addSubmitLogicOpts );
9192
9193 jQuery( '.frm_image_preview_wrapper' ).on( 'click', '.frm_choose_image_box', addImageToOption );
9194 jQuery( '.frm_image_preview_wrapper' ).on( 'click', '.frm_remove_image_option', removeImageFromOption );
9195
9196 // Close shortcode modal on click.
9197 formSettings.on( 'mouseup', '*:not(.frm-show-box)', function( e ) {
9198 e.stopPropagation();
9199 if ( e.target.classList.contains( 'frm-show-box' ) ) {
9200 return;
9201 }
9202 var sidebar = document.getElementById( 'frm_adv_info' ),
9203 isChild = jQuery( e.target ).closest( '#frm_adv_info' ).length > 0;
9204
9205 if ( sidebar.getAttribute( 'data-fills' ) === e.target.id && typeof e.target.id !== 'undefined' ) {
9206 return;
9207 }
9208
9209 if ( sidebar !== null && ! isChild && sidebar.display !== 'none' ) {
9210 hideShortcodes( sidebar );
9211 }
9212 });
9213
9214 //Warning when user selects "Do not store entries ..."
9215 jQuery( document.getElementById( 'no_save' ) ).on( 'change', function() {
9216 if ( this.checked ) {
9217 if ( confirm( frm_admin_js.no_save_warning ) !== true ) {
9218 // Uncheck box if user hits "Cancel"
9219 jQuery( this ).attr( 'checked', false );
9220 }
9221 }
9222 });
9223
9224 //Show/hide Messages header
9225 jQuery( '#editable, #edit_action, #save_draft, #success_action' ).on( 'change', function() {
9226 maybeShowFormMessages();
9227 });
9228 jQuery( 'select[name="options[success_action]"], select[name="options[edit_action]"]' ).on( 'change', showSuccessOpt );
9229
9230 $loggedIn = document.getElementById( 'logged_in' );
9231 jQuery( $loggedIn ).on( 'change', function() {
9232 if ( this.checked ) {
9233 visible( '.hide_logged_in' );
9234 } else {
9235 invisible( '.hide_logged_in' );
9236 }
9237 });
9238
9239 $cookieExp = jQuery( document.getElementById( 'frm_cookie_expiration' ) );
9240 jQuery( document.getElementById( 'frm_single_entry_type' ) ).on( 'change', function() {
9241 if ( this.value === 'cookie' ) {
9242 $cookieExp.fadeIn( 'slow' );
9243 } else {
9244 $cookieExp.fadeOut( 'slow' );
9245 }
9246 });
9247
9248 var $singleEntry = document.getElementById( 'single_entry' );
9249 jQuery( $singleEntry ).on( 'change', function() {
9250 if ( this.checked ) {
9251 visible( '.hide_single_entry' );
9252 } else {
9253 invisible( '.hide_single_entry' );
9254 }
9255
9256 if ( this.checked && jQuery( document.getElementById( 'frm_single_entry_type' ) ).val() === 'cookie' ) {
9257 $cookieExp.fadeIn( 'slow' );
9258 } else {
9259 $cookieExp.fadeOut( 'slow' );
9260 }
9261 });
9262
9263 jQuery( '.hide_save_draft' ).hide();
9264
9265 var $saveDraft = jQuery( document.getElementById( 'save_draft' ) );
9266 $saveDraft.on( 'change', function() {
9267 if ( this.checked ) {
9268 jQuery( '.hide_save_draft' ).fadeIn( 'slow' );
9269 } else {
9270 jQuery( '.hide_save_draft' ).fadeOut( 'slow' );
9271 }
9272 });
9273 triggerChange( $saveDraft );
9274
9275 //If Allow editing is checked/unchecked
9276 $editable = document.getElementById( 'editable' );
9277 jQuery( $editable ).on( 'change', function() {
9278 if ( this.checked ) {
9279 jQuery( '.hide_editable' ).fadeIn( 'slow' );
9280 triggerChange( document.getElementById( 'edit_action' ) );
9281 } else {
9282 jQuery( '.hide_editable' ).fadeOut( 'slow' );
9283 jQuery( '.edit_action_message_box' ).fadeOut( 'slow' );//Hide On Update message box
9284 }
9285 });
9286
9287 //If File Protection is checked/unchecked
9288 jQuery( document ).on( 'change', '#protect_files', function() {
9289 if ( this.checked ) {
9290 jQuery( '.hide_protect_files' ).fadeIn( 'slow' );
9291 } else {
9292 jQuery( '.hide_protect_files' ).fadeOut( 'slow' );
9293 }
9294 });
9295
9296 jQuery( document ).on( 'frm-multiselect-changed', '#protect_files_role', adjustVisibilityValuesForEveryoneValues );
9297
9298 jQuery( document ).on( 'submit', '.frm_form_settings', settingsSubmitted );
9299 jQuery( document ).on( 'change', '#form_settings_page input:not(.frm-search-input), #form_settings_page select, #form_settings_page textarea', fieldUpdated );
9300 jQuery( document ).on( 'click', '#frm_save_and_reload_settings', saveAndReloadSettings );
9301
9302 // Page Selection Autocomplete
9303 initSelectionAutocomplete();
9304 },
9305
9306 panelInit: function() {
9307 var customPanel, settingsPage, viewPage, insertFieldsTab;
9308
9309 jQuery( '.frm_wrap, #postbox-container-1' ).on( 'click', '.frm_insert_code', insertCode );
9310 jQuery( document ).on( 'change', '.frm_insert_val', function() {
9311 insertFieldCode( jQuery( this ).data( 'target' ), jQuery( this ).val() );
9312 jQuery( this ).val( '' );
9313 });
9314
9315 jQuery( document ).on( 'click change', '#frm-id-key-condition', resetLogicBuilder );
9316 jQuery( document ).on( 'keyup change', '.frm-build-logic', setLogicExample );
9317
9318 showInputIcon();
9319 jQuery( document ).on( 'frmElementAdded', function( event, parentEle ) {
9320 /* This is here for add-ons to trigger */
9321 showInputIcon( parentEle );
9322 });
9323 jQuery( document ).on( 'mousedown', '.frm-show-box', showShortcodes );
9324
9325 settingsPage = document.getElementById( 'form_settings_page' );
9326 viewPage = document.body.classList.contains( 'post-type-frm_display' );
9327 insertFieldsTab = document.getElementById( 'frm_insert_fields_tab' );
9328
9329 if ( settingsPage !== null || viewPage ) {
9330 jQuery( document ).on( 'focusin', 'form input, form textarea', function( e ) {
9331 var htmlTab;
9332 e.stopPropagation();
9333 maybeShowModal( this );
9334
9335 if ( jQuery( this ).is( ':not(:submit, input[type=button], .frm-search-input, input[type=checkbox])' ) ) {
9336 if ( jQuery( e.target ).closest( '#frm_adv_info' ).length ) {
9337 // Don't trigger for fields inside of the modal.
9338 return;
9339 }
9340
9341 if ( settingsPage !== null ) {
9342 /* form settings page */
9343 htmlTab = jQuery( '#frm_html_tab' );
9344 if ( jQuery( this ).closest( '#html_settings' ).length > 0 ) {
9345 htmlTab.show();
9346 htmlTab.siblings().hide();
9347 jQuery( '#frm_html_tab a' ).trigger( 'click' );
9348 toggleAllowedHTML( this, e.type );
9349 } else {
9350 showElement( jQuery( '.frm-category-tabs li' ) );
9351 insertFieldsTab.click();
9352 htmlTab.hide();
9353 htmlTab.siblings().show();
9354 }
9355 } else if ( viewPage ) {
9356 // Run on view page.
9357 toggleAllowedShortcodes( this.id, e.type );
9358 }
9359 }
9360 });
9361 }
9362
9363 jQuery( '.frm_wrap, #postbox-container-1' ).on( 'mousedown', '#frm_adv_info a, .frm_field_list a', function( e ) {
9364 e.preventDefault();
9365 });
9366
9367 customPanel = jQuery( '#frm_adv_info' );
9368 customPanel.on( 'click', '.subsubsub a.frmids', function( e ) {
9369 toggleKeyID( 'frmids', e );
9370 });
9371 customPanel.on( 'click', '.subsubsub a.frmkeys', function( e ) {
9372 toggleKeyID( 'frmkeys', e );
9373 });
9374 },
9375
9376 viewInit: function() {
9377 var $addRemove,
9378 $advInfo = jQuery( document.getElementById( 'frm_adv_info' ) );
9379 $advInfo.before( '<div id="frm_position_ele"></div>' );
9380 setupMenuOffset();
9381
9382 jQuery( document ).on( 'blur', '#param', checkDetailPageSlug );
9383 jQuery( document ).on( 'blur', 'input[name^="options[where_val]"]', checkFilterParamNames );
9384
9385 // Show loading indicator.
9386 jQuery( '#publish' ).on( 'mousedown', function() {
9387 fieldsUpdated = 0;
9388 this.classList.add( 'frm_loading_button' );
9389 });
9390
9391 // move content tabs
9392 jQuery( '#frm_dyncontent .handlediv' ).before( jQuery( '#frm_dyncontent .nav-menus-php' ) );
9393
9394 // click content tabs
9395 jQuery( '.nav-tab-wrapper a' ).on( 'click', clickContentTab );
9396
9397 // click tabs after panel is replaced with ajax
9398 jQuery( '#side-sortables' ).on( 'click', '.frm_doing_ajax.categorydiv .category-tabs a', clickTabsAfterAjax );
9399
9400 initToggleShortcodes();
9401 jQuery( '.frm_code_list:not(.frm-dropdown-menu) a' ).addClass( 'frm_noallow' );
9402
9403 jQuery( 'input[name="show_count"]' ).on( 'change', showCount );
9404
9405 jQuery( document.getElementById( 'form_id' ) ).on( 'change', displayFormSelected );
9406
9407 $addRemove = jQuery( '.frm_repeat_rows' );
9408 $addRemove.on( 'click', '.frm_add_order_row', addOrderRow );
9409 $addRemove.on( 'click', '.frm_add_where_row', addWhereRow );
9410 $addRemove.on( 'change', '.frm_insert_where_options', insertWhereOptions );
9411 $addRemove.on( 'change', '.frm_where_is_options', hideWhereOptions );
9412
9413 setDefaultPostStatus();
9414 },
9415
9416 inboxInit: function() {
9417 jQuery( '.frm_inbox_dismiss, footer .frm-button-secondary, footer .frm-button-primary' ).on( 'click', function( e ) {
9418 var message = this.parentNode.parentNode,
9419 key = message.getAttribute( 'data-message' ),
9420 href = this.getAttribute( 'href' );
9421
9422 if ( 'free_templates' === key && ! this.classList.contains( 'frm_inbox_dismiss' ) ) {
9423 return;
9424 }
9425
9426 e.preventDefault();
9427
9428 data = {
9429 action: 'frm_inbox_dismiss',
9430 key: key,
9431 nonce: frmGlobal.nonce
9432 };
9433 postAjax( data, function() {
9434 if ( href !== '#' ) {
9435 window.location = href;
9436 return true;
9437 }
9438 fadeOut( message, function() {
9439 message.parentNode.removeChild( message );
9440 });
9441 });
9442 });
9443 jQuery( '#frm-dismiss-inbox' ).on( 'click', function( e ) {
9444 data = {
9445 action: 'frm_inbox_dismiss',
9446 key: 'all',
9447 nonce: frmGlobal.nonce
9448 };
9449 postAjax( data, function() {
9450 fadeOut( document.getElementById( 'frm_message_list' ), function() {
9451 document.getElementById( 'frm_empty_inbox' ).classList.remove( 'frm_hidden' );
9452 });
9453 });
9454 });
9455 },
9456
9457 solutionInit: function() {
9458 jQuery( document ).on( 'submit', '#frm-new-template', installTemplate );
9459 },
9460
9461 styleInit: function() {
9462 const debouncedPreviewUpdate = debounce( changeStyling, 100 );
9463
9464 collapseAllSections();
9465
9466 document.getElementById( 'frm_field_height' ).addEventListener( 'change', textSquishCheck );
9467 document.getElementById( 'frm_field_font_size' ).addEventListener( 'change', textSquishCheck );
9468 document.getElementById( 'frm_field_pad' ).addEventListener( 'change', textSquishCheck );
9469
9470 jQuery( 'input.hex' ).wpColorPicker({
9471 change: function( event ) {
9472 if ( null !== event.target.getAttribute( 'data-alpha-color-type' ) ) {
9473 debouncedPreviewUpdate();
9474 return;
9475 } else {
9476 const hexcolor = jQuery( this ).wpColorPicker( 'color' );
9477 jQuery( event.target ).val( hexcolor ).trigger( 'change' );
9478 }
9479 }
9480 });
9481 jQuery( '.wp-color-result-text' ).text( function( i, oldText ) {
9482 return oldText === 'Select Color' ? 'Select' : oldText;
9483 });
9484
9485 function changeStyling() {
9486 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();
9487 locStr = JSON.stringify( locStr );
9488 jQuery.ajax({
9489 type: 'POST', url: ajaxurl,
9490 data: {
9491 action: 'frm_change_styling',
9492 nonce: frmGlobal.nonce,
9493 frm_style_setting: locStr
9494 },
9495 success: function( css ) {
9496 document.getElementById( 'this_css' ).innerHTML = css;
9497 }
9498 });
9499 }
9500
9501 // update styling on change
9502 jQuery( '#frm_styling_form .styling_settings' ).on( 'change', debouncedPreviewUpdate );
9503
9504 // menu tabs
9505 jQuery( '#menu-settings-column' ).on( 'click', function( e ) {
9506 var panelId, wrapper,
9507 target = jQuery( e.target );
9508
9509 if ( e.target.className.indexOf( 'nav-tab-link' ) !== -1 ) {
9510
9511 panelId = target.data( 'type' );
9512
9513 wrapper = target.parents( '.accordion-section-content' ).first();
9514
9515
9516 jQuery( '.tabs-panel-active', wrapper ).removeClass( 'tabs-panel-active' ).addClass( 'tabs-panel-inactive' );
9517 jQuery( '#' + panelId, wrapper ).removeClass( 'tabs-panel-inactive' ).addClass( 'tabs-panel-active' );
9518
9519 jQuery( '.tabs', wrapper ).removeClass( 'tabs' );
9520 target.parent().addClass( 'tabs' );
9521
9522 // select the search bar
9523 jQuery( '.quick-search', wrapper ).trigger( 'focus' );
9524
9525 e.preventDefault();
9526 }
9527 });
9528
9529 jQuery( document ).on( 'change', '.frm-dropdown-menu input[type="radio"]', function() {
9530 const radio = this;
9531 const btnGrp = this.closest( '.btn-group' );
9532 const btnId = btnGrp.getAttribute( 'id' );
9533
9534 const select = document.getElementById( btnId.replace( '_select', '' ) );
9535 if ( select ) {
9536 select.value = radio.value;
9537 }
9538
9539 jQuery( btnGrp ).children( 'button' ).html( radio.nextElementSibling.innerHTML + ' <b class="caret"></b>' );
9540
9541 const activeItem = btnGrp.querySelector( '.dropdown-item.active' );
9542 if ( activeItem ) {
9543 activeItem.classList.remove( 'active' );
9544 }
9545
9546 this.closest( '.dropdown-item' ).classList.add( 'active' );
9547 });
9548
9549 jQuery( '#frm_confirm_modal' ).on( 'click', '[data-resetstyle]', function( e ) {
9550 var button = document.getElementById( 'frm_reset_style' );
9551
9552 button.classList.add( 'frm_loading_button' );
9553 e.stopPropagation();
9554
9555 jQuery.ajax({
9556 type: 'POST', url: ajaxurl,
9557 data: {action: 'frm_settings_reset', nonce: frmGlobal.nonce},
9558 success: function( errObj ) {
9559 var key;
9560 errObj = errObj.replace( /^\s+|\s+$/g, '' );
9561 if ( errObj.indexOf( '{' ) === 0 ) {
9562 errObj = JSON.parse( errObj );
9563 }
9564 for ( key in errObj ) {
9565 jQuery( 'input[name$="[' + key + ']"], select[name$="[' + key + ']"]' ).val( errObj[key]);
9566 }
9567 jQuery( '#frm_submit_style, #frm_auto_width' ).prop( 'checked', false );
9568 triggerChange( document.getElementById( 'frm_fieldset' ) );
9569 button.classList.remove( 'frm_loading_button' );
9570 }
9571 });
9572 });
9573
9574 jQuery( '.frm_pro_form #datepicker_sample' ).datepicker({ changeMonth: true, changeYear: true });
9575
9576 jQuery( document.getElementById( 'frm_position' ) ).on( 'change', setPosClass );
9577
9578 jQuery( '.frm_image_preview_wrapper' ).on( 'click', '.frm_choose_image_box', addImageToOption );
9579 jQuery( '.frm_image_preview_wrapper' ).on( 'click', '.frm_remove_image_option', removeImageFromOption );
9580 },
9581
9582 customCSSInit: function() {
9583 console.warn( 'Calling frmAdminBuild.customCSSInit is deprecated.' );
9584 },
9585
9586 globalSettingsInit: function() {
9587 var licenseTab;
9588
9589 jQuery( document ).on( 'click', '[data-frmuninstall]', uninstallNow );
9590
9591 initiateMultiselect();
9592
9593 // activate addon licenses
9594 licenseTab = document.getElementById( 'licenses_settings' );
9595 if ( licenseTab !== null ) {
9596 jQuery( licenseTab ).on( 'click', '.edd_frm_save_license', saveAddonLicense );
9597 }
9598
9599 // Solution install page
9600 jQuery( document ).on( 'click', '#frm-new-template button', installTemplateFieldset );
9601
9602 jQuery( '#frm-dismissable-cta .dismiss' ).on( 'click', function( event ) {
9603 event.preventDefault();
9604 jQuery.post( ajaxurl, {
9605 action: 'frm_lite_settings_upgrade'
9606 });
9607 jQuery( '.settings-lite-cta' ).remove();
9608 });
9609
9610 const captchaType = document.getElementById( 'frm_re_type' );
9611 if ( captchaType ) {
9612 captchaType.addEventListener( 'change', handleCaptchaTypeChange );
9613 }
9614 },
9615
9616 exportInit: function() {
9617 jQuery( '.frm_form_importer' ).on( 'submit', startFormMigration );
9618 jQuery( document.getElementById( 'frm_export_xml' ) ).on( 'submit', validateExport );
9619 jQuery( '#frm_export_xml input, #frm_export_xml select' ).on( 'change', removeExportError );
9620 jQuery( 'input[name="frm_import_file"]' ).on( 'change', checkCSVExtension );
9621 jQuery( 'select[name="format"]' ).on( 'change', checkExportTypes ).trigger( 'change' );
9622 jQuery( 'input[name="frm_export_forms[]"]' ).on( 'click', preventMultipleExport );
9623 initiateMultiselect();
9624
9625 jQuery( '.frm-feature-banner .dismiss' ).on( 'click', function( event ) {
9626 event.preventDefault();
9627 jQuery.post( ajaxurl, {
9628 action: 'frm_dismiss_migrator',
9629 plugin: this.id,
9630 nonce: frmGlobal.nonce
9631 });
9632 this.parentElement.remove();
9633 });
9634 },
9635
9636 inboxBannerInit: function() {
9637 const banner = document.getElementById( 'frm_banner' );
9638 if ( ! banner ) {
9639 return;
9640 }
9641
9642 const dismissButton = banner.querySelector( '.frm-banner-dismiss' );
9643 document.addEventListener(
9644 'click',
9645 function( event ) {
9646 if ( event.target !== dismissButton ) {
9647 return;
9648 }
9649
9650 const data = {
9651 action: 'frm_inbox_dismiss',
9652 key: banner.dataset.key,
9653 nonce: frmGlobal.nonce
9654 };
9655 postAjax(
9656 data,
9657 function() {
9658 jQuery( banner ).fadeOut(
9659 400,
9660 function() {
9661 banner.remove();
9662 }
9663 );
9664 }
9665 );
9666 }
9667 );
9668 },
9669
9670 updateOpts: function( fieldId, opts, modal ) {
9671 var separate = usingSeparateValues( fieldId ),
9672 action = isProductField( fieldId ) ? 'frm_bulk_products' : 'frm_import_options';
9673 jQuery.ajax({
9674 type: 'POST',
9675 url: ajaxurl,
9676 data: {
9677 action: action,
9678 field_id: fieldId,
9679 opts: opts,
9680 separate: separate,
9681 nonce: frmGlobal.nonce
9682 },
9683 success: function( html ) {
9684 document.getElementById( 'frm_field_' + fieldId + '_opts' ).innerHTML = html;
9685 resetDisplayedOpts( fieldId );
9686
9687 if ( typeof modal !== 'undefined' ) {
9688 modal.dialog( 'close' );
9689 document.getElementById( 'frm-update-bulk-opts' ).classList.remove( 'frm_loading_button' );
9690 }
9691 }
9692 });
9693 },
9694
9695 /* remove conditional logic if the field doesn't exist */
9696 triggerRemoveLogic: function( fieldID, metaName ) {
9697 jQuery( '#frm_logic_' + fieldID + '_' + metaName + ' .frm_remove_tag' ).trigger( 'click' );
9698 },
9699
9700 downloadXML: function( controller, ids, isTemplate ) {
9701 var url = ajaxurl + '?action=frm_' + controller + '_xml&ids=' + ids;
9702 if ( isTemplate !== null ) {
9703 url = url + '&is_template=' + isTemplate;
9704 }
9705 location.href = url;
9706 },
9707
9708 /**
9709 * @since 5.0.04
9710 */
9711 hooks: {
9712 applyFilters: function( hookName, ...args ) {
9713 return wp.hooks.applyFilters( hookName, ...args );
9714 },
9715 addFilter: function( hookName, callback, priority ) {
9716 return wp.hooks.addFilter( hookName, 'formidable', callback, priority );
9717 }
9718 }
9719 };
9720 }
9721
9722 frmAdminBuild = frmAdminBuildJS();
9723
9724 jQuery( document ).ready(
9725 () => {
9726 frmAdminBuild.init();
9727
9728 frmDom.bootstrap.setupBootstrapDropdowns( convertOldBootstrapDropdownsToBootstrap4 );
9729 function convertOldBootstrapDropdownsToBootstrap4( frmDropdownMenu ) {
9730 const toggle = frmDropdownMenu.querySelector( '.frm-dropdown-toggle' );
9731 if ( toggle ) {
9732 if ( ! toggle.hasAttribute( 'role' ) ) {
9733 toggle.setAttribute( 'role', 'button' );
9734 }
9735 if ( ! toggle.hasAttribute( 'tabindex' ) ) {
9736 toggle.setAttribute( 'tabindex', 0 );
9737 }
9738 }
9739
9740 // Convert <li> and <ul> tags.
9741 if ( 'UL' === frmDropdownMenu.tagName ) {
9742 convertBootstrapUl( frmDropdownMenu );
9743 }
9744 }
9745
9746 function convertBootstrapUl( ul ) {
9747 let html = ul.outerHTML;
9748 html = html.replace( '<ul ', '<div ' );
9749 html = html.replace( '</ul>', '</div>' );
9750 html = html.replaceAll( '<li>', '<div class="dropdown-item">' );
9751 html = html.replaceAll( '<li class="', '<div class="dropdown-item ' );
9752 html = html.replaceAll( '</li>', '</div>' );
9753 ul.outerHTML = html;
9754 }
9755 }
9756 );
9757
9758 function frm_remove_tag( htmlTag ) { // eslint-disable-line camelcase
9759 console.warn( 'DEPRECATED: function frm_remove_tag in v2.0' );
9760 jQuery( htmlTag ).remove();
9761 }
9762
9763 function frm_show_div( div, value, showIf, classId ) { // eslint-disable-line camelcase
9764 if ( value == showIf ) {
9765 jQuery( classId + div ).fadeIn( 'slow' ).css( 'visibility', 'visible' );
9766 } else {
9767 jQuery( classId + div ).fadeOut( 'slow' );
9768 }
9769 }
9770
9771 function frmCheckAll( checked, n ) {
9772 jQuery( 'input[name^="' + n + '"]' ).prop( 'checked', ! ! checked );
9773 }
9774
9775 function frmCheckAllLevel( checked, n, level ) {
9776 var $kids = jQuery( '.frm_catlevel_' + level ).children( '.frm_checkbox' ).children( 'label' );
9777 $kids.children( 'input[name^="' + n + '"]' ).prop( 'checked', ! ! checked );
9778 }
9779
9780 function frm_add_logic_row( id, formId ) { // eslint-disable-line camelcase
9781 console.warn( 'DEPRECATED: function frm_add_logic_row in v2.0' );
9782 jQuery.ajax({
9783 type: 'POST',
9784 url: ajaxurl,
9785 data: {
9786 action: 'frm_add_logic_row',
9787 form_id: formId,
9788 field_id: id,
9789 meta_name: jQuery( '#frm_logic_row_' + id + ' > div' ).length,
9790 nonce: frmGlobal.nonce
9791 },
9792 success: function( html ) {
9793 jQuery( '#frm_logic_row_' + id ).append( html );
9794 }
9795 });
9796 return false;
9797 }
9798
9799 function frmGetFieldValues( fieldId, cur, rowNumber, fieldType, htmlName ) {
9800
9801 if ( fieldId ) {
9802 jQuery.ajax({
9803 type: 'POST', url: ajaxurl,
9804 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,
9805 success: function( msg ) {
9806 document.getElementById( 'frm_show_selected_values_' + cur + '_' + rowNumber ).innerHTML = msg;
9807 }
9808 });
9809 }
9810 }
9811
9812 function frmImportCsv( formID ) {
9813 var urlVars = '';
9814 if ( typeof __FRMURLVARS !== 'undefined' ) {
9815 urlVars = __FRMURLVARS;
9816 }
9817
9818 jQuery.ajax({
9819 type: 'POST', url: ajaxurl,
9820 data: 'action=frm_import_csv&nonce=' + frmGlobal.nonce + '&frm_skip_cookie=1' + urlVars,
9821 success: function( count ) {
9822 var max = jQuery( '.frm_admin_progress_bar' ).attr( 'aria-valuemax' );
9823 var imported = max - count;
9824 var percent = ( imported / max ) * 100;
9825 jQuery( '.frm_admin_progress_bar' ).css( 'width', percent + '%' ).attr( 'aria-valuenow', imported );
9826
9827 if ( parseInt( count, 10 ) > 0 ) {
9828 jQuery( '.frm_csv_remaining' ).html( count );
9829 frmImportCsv( formID );
9830 } else {
9831 jQuery( document.getElementById( 'frm_import_message' ) ).html( frm_admin_js.import_complete );
9832 setTimeout( function() {
9833 location.href = '?page=formidable-entries&frm_action=list&form=' + formID + '&import-message=1';
9834 }, 2000 );
9835 }
9836 }
9837 });
9838 }
9839