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

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