PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 5.2
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v5.2
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / js / formidable_admin.js

formidable_admin.js in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 5.2, at js/formidable_admin.js

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