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

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