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

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

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