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

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