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

6,166 lines 185.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' ) ).click( app.deauthorize );
32 $( '.frm_authorize_link' ).click( app.authorize );
33 if ( el.reset !== null ) {
34 $( el.reset ).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
56 /**
57 * Go to upgrade url.
58 *
59 * @since 4.03
60 */
61 gotoUpgradeUrl: function() {
62 var w = window.open( frmGlobal.app_url + '/api-connect/', '_blank', 'location=no,width=500,height=730,scrollbars=0' );
63 w.focus();
64 },
65
66 updateForm: function( response ) {
67
68 // Start spinner.
69 var btn = el.btn;
70 btn.classList.add( 'frm_loading_button' );
71
72 if ( response.url !== '' ) {
73 app.showProgress({
74 success: true,
75 message: 'Installing...'
76 });
77 var fallback = setTimeout( function() {
78 app.showProgress({
79 success: true,
80 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>'
81 });
82 }, 10000 );
83 $.ajax({
84 type: 'POST',
85 url: ajaxurl,
86 dataType: 'json',
87 data: {
88 action: 'frm_connect',
89 plugin: response.url,
90 nonce: frmGlobal.nonce
91 },
92 success: function() {
93 clearTimeout( fallback );
94 app.activateKey( response );
95 },
96 error: function( xhr, textStatus, e ) {
97 clearTimeout( fallback );
98 btn.classList.remove( 'frm_loading_button' );
99 app.showMessage({
100 success: false,
101 message: e
102 });
103 }
104 });
105 } else if ( response.key !== '' ) {
106 app.activateKey( response );
107 }
108 },
109
110 activateKey: function( response ) {
111 var btn = el.btn;
112 if ( response.key === '' ) {
113 btn.classList.remove( 'frm_loading_button' );
114 } else {
115 app.showProgress({
116 success: true,
117 message: 'Activating...'
118 });
119 $.ajax({
120 type: 'POST',
121 url: ajaxurl,
122 dataType: 'json',
123 data: {
124 action: 'frm_addon_activate',
125 license: response.key,
126 plugin: 'formidable_pro',
127 wpmu: 0,
128 nonce: frmGlobal.nonce
129 },
130 success: function( msg ) {
131 btn.classList.remove( 'frm_loading_button' );
132
133 if ( msg.success === true ) {
134 el.licenseBox.classList.replace( 'frm_unauthorized_box', 'frm_authorized_box' );
135 }
136
137 app.showMessage( msg );
138 },
139 error: function( xhr, textStatus, e ) {
140 btn.classList.remove( 'frm_loading_button' );
141 app.showMessage({
142 success: false,
143 message: e
144 });
145 }
146 });
147 }
148 },
149
150 /* Manual license authorization */
151 authorize: function() {
152 /*jshint validthis:true */
153 var button = this;
154 var pluginSlug = this.getAttribute( 'data-plugin' );
155 var input = document.getElementById( 'edd_' + pluginSlug + '_license_key' );
156 var license = input.value;
157 var wpmu = document.getElementById( 'proplug-wpmu' );
158 this.classList.add( 'frm_loading_button' );
159 if ( wpmu === null ) {
160 wpmu = 0;
161 } else if ( wpmu.checked ) {
162 wpmu = 1;
163 } else {
164 wpmu = 0;
165 }
166
167 $.ajax({
168 type: 'POST', url: ajaxurl, dataType: 'json',
169 data: {
170 action: 'frm_addon_activate',
171 license: license,
172 plugin: pluginSlug,
173 wpmu: wpmu,
174 nonce: frmGlobal.nonce
175 },
176 success: function( msg ) {
177 app.afterAuthorize( msg, input );
178 button.classList.remove( 'frm_loading_button' );
179 }
180 });
181 },
182
183 afterAuthorize: function( msg, input ) {
184 if ( msg.success === true ) {
185 input.value = '•••••••••••••••••••';
186 }
187
188 app.showMessage( msg );
189 },
190
191 showProgress: function( msg ) {
192 var messageBox = el.messageBox;
193 if ( msg.success === true ) {
194 messageBox.classList.remove( 'frm_error_style' );
195 messageBox.classList.add( 'frm_message', 'frm_updated_message' );
196 } else {
197 messageBox.classList.add( 'frm_error_style' );
198 messageBox.classList.remove( 'frm_message', 'frm_updated_message' );
199 }
200 messageBox.classList.remove( 'frm_hidden' );
201 messageBox.innerHTML = msg.message;
202 },
203
204 showMessage: function( msg ) {
205 var messageBox = el.messageBox;
206
207 if ( msg.success === true ) {
208 var d = el.licenseBox;
209 d.className = d.className.replace( 'frm_unauthorized_box', 'frm_authorized_box' );
210 messageBox.classList.remove( 'frm_error_style' );
211 messageBox.classList.add( 'frm_message', 'frm_updated_message' );
212 } else {
213 messageBox.classList.add( 'frm_error_style' );
214 messageBox.classList.remove( 'frm_message', 'frm_updated_message' );
215 }
216
217 messageBox.classList.remove( 'frm_hidden' );
218 messageBox.innerHTML = msg.message;
219 if ( msg.message !== '' ) {
220 setTimeout( function() {
221 messageBox.innerHTML = '';
222 messageBox.classList.add( 'frm_hidden' );
223 messageBox.classList.remove( 'frm_error_style', 'frm_message', 'frm_updated_message' );
224 }, 10000 );
225 }
226 },
227
228 /* Clear the site license cache */
229 reauthorize: function() {
230 /*jshint validthis:true */
231 this.innerHTML = '<span class="frm-wait frm_spinner" style="visibility:visible;float:none"></span>';
232
233 $.ajax({
234 type: 'POST',
235 url: ajaxurl,
236 dataType: 'json',
237 data: {
238 action: 'frm_reset_cache',
239 plugin: 'formidable_pro',
240 nonce: frmGlobal.nonce
241 },
242 success: function( msg ) {
243 el.reset.innerHTML = msg.message;
244 if ( el.reset.getAttribute( 'data-refresh' ) === '1' ) {
245 window.location.reload();
246 }
247 }
248 });
249 return false;
250 },
251
252 deauthorize: function() {
253 /*jshint validthis:true */
254 if ( ! confirm( frmGlobal.deauthorize ) ) {
255 return false;
256 }
257 var pluginSlug = this.getAttribute( 'data-plugin' ),
258 input = document.getElementById( 'edd_' + pluginSlug + '_license_key' ),
259 license = input.value,
260 link = this;
261
262 this.innerHTML = '<span class="frm-wait frm_spinner" style="visibility:visible;"></span>';
263
264 $.ajax({
265 type: 'POST',
266 url: ajaxurl,
267 data: {
268 action: 'frm_addon_deactivate',
269 license: license,
270 plugin: pluginSlug,
271 nonce: frmGlobal.nonce
272 },
273 success: function() {
274 el.licenseBox.className = el.licenseBox.className.replace( 'frm_authorized_box', 'frm_unauthorized_box' );
275 input.value = '';
276 link.innerHTML = '';
277 }
278 });
279 return false;
280 }
281 };
282
283 // Provide access to public functions/properties.
284 return app;
285
286 }( document, window, jQuery ) );
287
288 function frmAdminBuildJS() {
289 //'use strict';
290
291 /*global jQuery:false, frm_admin_js, frmGlobal, ajaxurl */
292
293 var $newFields = jQuery( document.getElementById( 'frm-show-fields' ) );
294 var builderForm = document.getElementById( 'new_fields' );
295 var thisForm = document.getElementById( 'form_id' );
296 var cancelSort = false;
297 var copyHelper = false;
298
299 var thisFormId = 0;
300 if ( thisForm !== null ) {
301 thisFormId = thisForm.value;
302 }
303
304 // Global settings
305 var s;
306
307 function showElement( element ) {
308 element[0].style.display = '';
309 }
310
311 function hideElement( element ) {
312 element[0].style.display = 'none';
313 }
314
315 function empty( $obj ) {
316 if ( $obj !== null ) {
317 while ( $obj.firstChild ) {
318 $obj.removeChild( $obj.firstChild );
319 }
320 }
321 }
322
323 function addClass( $obj, className ) {
324 if ( $obj.classList ) {
325 $obj.classList.add( className );
326 } else {
327 $obj.className += ' ' + className;
328 }
329 }
330
331 function confirmClick( e ) {
332 /*jshint validthis:true */
333 e.stopPropagation();
334 e.preventDefault();
335 confirmLinkClick( this );
336 }
337
338 function confirmLinkClick( link ) {
339 var message = link.getAttribute( 'data-frmverify' );
340
341 if ( message === null || link.id === 'frm-confirmed-click' ) {
342 return true;
343 } else {
344 return confirmModal( link );
345 }
346 }
347
348 function confirmModal( link ) {
349 var i, dataAtts,
350 $info = initModal( '#frm_confirm_modal', '400px' ),
351 continueButton = document.getElementById( 'frm-confirmed-click' );
352
353 if ( $info === false ) {
354 return false;
355 }
356
357 var caution = link.getAttribute( 'data-frmcaution' );
358 var cautionHtml = caution ? '<span class="frm-caution">' + caution + '</span> ' : '';
359
360 jQuery( '.frm-confirm-msg' ).html( cautionHtml + link.getAttribute( 'data-frmverify' ) );
361
362 removeAtts = continueButton.dataset;
363 for ( i in dataAtts ) {
364 continueButton.removeAttribute( 'data-' + i );
365 }
366
367 dataAtts = link.dataset;
368 for ( i in dataAtts ) {
369 if ( i !== 'frmverify' ) {
370 continueButton.setAttribute( 'data-' + i, dataAtts[i]);
371 }
372 }
373
374 $info.dialog( 'open' );
375 continueButton.setAttribute( 'href', link.getAttribute( 'href' ) );
376 return false;
377 }
378
379 function infoModal( msg ) {
380 var $info = initModal( '#frm_info_modal', '400px' );
381
382 if ( $info === false ) {
383 return false;
384 }
385
386 jQuery( '.frm-info-msg' ).html( msg );
387
388 $info.dialog( 'open' );
389 return false;
390 }
391
392 function toggleItem( e ) {
393 /*jshint validthis:true */
394 var toggle = this.getAttribute( 'data-frmtoggle' ),
395 text = this.getAttribute( 'data-toggletext' ),
396 items = jQuery( toggle );
397
398 e.preventDefault();
399
400 if ( items.is( ':visible' ) ) {
401 items.show();
402 } else {
403 items.hide();
404 }
405
406 if ( text !== null && text !== '' ) {
407 this.setAttribute( 'data-toggletext', this.innerHTML );
408 this.innerHTML = text;
409 }
410
411 return false;
412 }
413
414 function hideShowItem( e ) {
415 /*jshint validthis:true */
416 var hide = this.getAttribute( 'data-frmhide' ),
417 show = this.getAttribute( 'data-frmshow' ),
418 toggleClass = this.getAttribute( 'data-toggleclass' );
419
420 e.preventDefault();
421 if ( toggleClass === null ) {
422 toggleClass = 'frm_hidden';
423 }
424
425 if ( hide !== null ) {
426 jQuery( hide ).addClass( toggleClass );
427 }
428
429 if ( show !== null ) {
430 jQuery( show ).removeClass( toggleClass );
431 }
432
433 var current = this.parentNode.querySelectorAll( 'a.current' );
434 if ( current !== null ) {
435 for ( var i = 0; i < current.length; i++ ) {
436 current[ i ].classList.remove( 'current' );
437 }
438 this.classList.add( 'current' );
439 }
440
441 return false;
442 }
443
444 function setupMenuOffset() {
445 window.onscroll = document.documentElement.onscroll = setMenuOffset;
446 setMenuOffset();
447 }
448
449 function setMenuOffset() {
450 var fields = document.getElementById( 'frm_adv_info' );
451 if ( fields === null ) {
452 return;
453 }
454
455 var currentOffset = document.documentElement.scrollTop || document.body.scrollTop; // body for Safari
456 if ( currentOffset === 0 ) {
457 fields.classList.remove( 'frm_fixed' );
458 return;
459 }
460
461 var posEle = document.getElementById( 'frm_position_ele' );
462 if ( posEle === null ) {
463 return;
464 }
465
466 var eleOffset = jQuery( posEle ).offset();
467 var offset = eleOffset.top;
468 var desiredOffset = offset - currentOffset;
469 var menuHeight = 0;
470
471 var menu = document.getElementById( 'wpadminbar' );
472 if ( menu !== null ) {
473 menuHeight = menu.offsetHeight;
474 }
475
476 if ( desiredOffset < menuHeight ) {
477 desiredOffset = menuHeight;
478 }
479
480 if ( desiredOffset > menuHeight ) {
481 fields.classList.remove( 'frm_fixed' );
482 } else {
483 fields.classList.add( 'frm_fixed' );
484 if ( desiredOffset !== 32 ) {
485 fields.style.top = desiredOffset + 'px';
486 }
487 }
488 }
489
490 function loadTooltips() {
491 var wrapClass = jQuery( '.wrap, .frm_wrap' ),
492 confirmModal = document.getElementById( 'frm_confirm_modal' );
493
494 jQuery( confirmModal ).on( 'click', '[data-deletefield]', deleteFieldConfirmed );
495 jQuery( confirmModal ).on( 'click', '[data-removeid]', removeThisTag );
496 jQuery( confirmModal ).on( 'click', '[data-trashtemplate]', trashTemplate );
497
498 wrapClass.on( 'click', '.frm_remove_tag, .frm_remove_form_action', removeThisTag );
499 wrapClass.on( 'click', 'a[data-frmverify]', confirmClick );
500 wrapClass.on( 'click', 'a[data-frmtoggle]', toggleItem );
501 wrapClass.on( 'click', 'a[data-frmhide], a[data-frmshow]', hideShowItem );
502 wrapClass.on( 'click', '.widget-top,a.widget-action', clickWidget );
503
504 wrapClass.on( 'mouseenter.frm', '.frm_bstooltip, .frm_help', function() {
505 jQuery( this ).off( 'mouseenter.frm' );
506
507 jQuery( '.frm_bstooltip, .frm_help' ).tooltip( );
508 jQuery( this ).tooltip( 'show' );
509 });
510
511 jQuery( '.frm_bstooltip, .frm_help' ).tooltip( );
512 }
513
514 function removeThisTag() {
515 /*jshint validthis:true */
516 var show, hide, removeMore,
517 id = '',
518 deleteButton = jQuery( this ),
519 continueRemove = confirmLinkClick( this );
520
521 if ( continueRemove === false ) {
522 return;
523 } else {
524 id = deleteButton.attr( 'data-removeid' );
525 show = deleteButton.attr( 'data-showlast' );
526 removeMore = deleteButton.attr( 'data-removemore' );
527 if ( typeof show === 'undefined' ) {
528 show = '';
529 }
530 hide = deleteButton.attr( 'data-hidelast' );
531 if ( typeof hide === 'undefined' ) {
532 hide = '';
533 }
534 }
535
536 if ( show !== '' ) {
537 if ( deleteButton.closest( '.frm_add_remove' ).find( '.frm_remove_tag:visible' ).length > 1 ) {
538 show = '';
539 hide = '';
540 }
541 } else if ( id.indexOf( 'frm_postmeta_' ) === 0 ) {
542 if ( jQuery( '#frm_postmeta_rows .frm_postmeta_row' ).length < 2 ) {
543 show = '.frm_add_postmeta_row.button';
544 }
545 if ( jQuery( '.frm_toggle_cf_opts' ).length && jQuery( '#frm_postmeta_rows .frm_postmeta_row:not(#' + id + ')' ).last().length ) {
546 if ( show !== '' ) {
547 show += ',';
548 }
549 show += '#' + jQuery( '#frm_postmeta_rows .frm_postmeta_row:not(#' + id + ')' ).last().attr( 'id' ) + ' .frm_toggle_cf_opts';
550 }
551 }
552
553 var $fadeEle = jQuery( document.getElementById( id ) );
554 $fadeEle.fadeOut( 400, function() {
555 $fadeEle.remove();
556
557 if ( hide !== '' ) {
558 jQuery( hide ).hide();
559 }
560
561 if ( show !== '' ) {
562 jQuery( show + ' a,' + show ).removeClass( 'frm_hidden' ).fadeIn( 'slow' );
563 }
564
565 var action = jQuery( this ).closest( '.frm_form_action_settings' );
566 if ( typeof action !== 'undefined' ) {
567 var type = jQuery( this ).closest( '.frm_form_action_settings' ).find( '.frm_action_name' ).val();
568 checkActiveAction( type );
569 }
570 });
571
572 if ( typeof removeMore !== 'undefined' ) {
573 removeMore = jQuery( removeMore );
574 removeMore.fadeOut( 400, function() {
575 removeMore.remove();
576 });
577 }
578
579 if ( show !== '' ) {
580 jQuery( this ).closest( '.frm_logic_rows' ).fadeOut( 'slow' );
581 }
582
583 return false;
584 }
585
586 function clickWidget( event, b ) {
587 /*jshint validthis:true */
588 var target = event.target;
589 if ( typeof b === 'undefined' ) {
590 b = this;
591 }
592
593 popCalcFields( b, false );
594
595 var cont = jQuery( b ).closest( '.frm_form_action_settings' );
596 if ( cont.length && typeof target !== 'undefined' && ( target.parentElement.className.indexOf( 'frm_email_icons' ) > -1 || target.parentElement.className.indexOf( 'frm_toggle' ) > -1 ) ) {
597 // clicking on delete icon shouldn't open it
598 event.stopPropagation();
599 return;
600 }
601
602 var inside = cont.children( '.widget-inside' );
603
604 if ( cont.length && inside.find( 'p, div, table' ).length < 1 ) {
605 var actionId = cont.find( 'input[name$="[ID]"]' ).val();
606 var actionType = cont.find( 'input[name$="[post_excerpt]"]' ).val();
607 if ( actionType ) {
608 inside.html( '<span class="frm-wait frm_spinner"></span>' );
609 cont.find( '.spinner' ).fadeIn( 'slow' );
610 jQuery.ajax({
611 type: 'POST',
612 url: ajaxurl,
613 data: {
614 action: 'frm_form_action_fill',
615 action_id: actionId,
616 action_type: actionType,
617 nonce: frmGlobal.nonce
618 },
619 success: function( html ) {
620 inside.html( html );
621 initiateMultiselect();
622 showInputIcon( '#' + cont.attr( 'id' ) );
623 }
624 });
625 }
626 }
627
628 jQuery( b ).closest( '.frm_field_box' ).siblings().find( '.widget-inside' ).slideUp( 'fast' );
629 if ( ( typeof b.className !== 'undefined' && b.className.indexOf( 'widget-action' ) !== -1 ) || jQuery( b ).closest( '.start_divider' ).length < 1 ) {
630 return;
631 }
632
633 inside = jQuery( b ).closest( 'div.widget' ).children( '.widget-inside' );
634 if ( inside.is( ':hidden' ) ) {
635 inside.slideDown( 'fast' );
636 } else {
637 inside.slideUp( 'fast' );
638 }
639 }
640
641 function clickNewTab() {
642 /*jshint validthis:true */
643 var t = this.getAttribute( 'href' ),
644 c = t.replace( '#', '.' ),
645 $link = jQuery( this );
646
647 if ( typeof t === 'undefined' ) {
648 return false;
649 }
650
651 $link.closest( 'li' ).addClass( 'frm-tabs active' ).siblings( 'li' ).removeClass( 'frm-tabs active starttab' );
652 $link.closest( 'div' ).children( '.tabs-panel' ).not( t ).not( c ).hide();
653 document.getElementById( t.replace( '#', '' ) ).style.display = 'block';
654
655 if ( this.id === 'frm_insert_fields_tab' ) {
656 clearSettingsBox();
657 }
658 return false;
659 }
660
661 function clickTab( link, auto ) {
662 link = jQuery( link );
663 var t = link.attr( 'href' );
664 if ( typeof t === 'undefined' ) {
665 return;
666 }
667
668 var c = t.replace( '#', '.' );
669
670 link.closest( 'li' ).addClass( 'frm-tabs active' ).siblings( 'li' ).removeClass( 'frm-tabs active starttab' );
671 if ( link.closest( 'div' ).find( '.tabs-panel' ).length ) {
672 link.closest( 'div' ).children( '.tabs-panel' ).not( t ).not( c ).hide();
673 } else {
674 if ( document.getElementById( 'form_global_settings' ) !== null ) {
675 /* global settings */
676 var ajax = link.data( 'frmajax' );
677 link.closest( '.frm_wrap' ).find( '.tabs-panel, .hide_with_tabs' ).hide();
678 if ( typeof ajax !== 'undefined' && ajax == '1' ) {
679 loadSettingsTab( t );
680 }
681 } else {
682 /* form settings page */
683 jQuery( '#frm-categorydiv .tabs-panel, .hide_with_tabs' ).hide();
684 }
685 }
686 jQuery( t ).show();
687 jQuery( c ).show();
688
689 hideShortcodes();
690
691 if ( auto !== 'auto' ) {
692 // Hide success message on tab change.
693 jQuery( '.frm_updated_message' ).hide();
694 jQuery( '.frm_warning_style' ).hide();
695 }
696
697 if ( jQuery( link ).closest( '#frm_adv_info' ).length ) {
698 return;
699 }
700
701 if ( jQuery( '.frm_form_settings' ).length ) {
702 jQuery( '.frm_form_settings' ).attr( 'action', '?page=formidable&frm_action=settings&id=' + jQuery( '.frm_form_settings input[name="id"]' ).val() + '&t=' + t.replace( '#', '' ) );
703 } else {
704 jQuery( '.frm_settings_form' ).attr( 'action', '?page=formidable-settings&t=' + t.replace( '#', '' ) );
705 }
706 }
707
708 /* Form Builder */
709 function setupSortable( sort ) {
710 var startSort = false,
711 container = jQuery( '#post-body-content' );
712
713 var opts = {
714 connectWith: 'ul.frm_sorting',
715 items: '> li.frm_field_box',
716 placeholder: 'sortable-placeholder',
717 axis: 'y',
718 cancel: '.widget,.frm_field_opts_list,input,textarea,select,.edit_field_type_end_divider,.frm_sortable_field_opts,.frm_noallow',
719 accepts: 'field_type_list',
720 forcePlaceholderSize: false,
721 tolerance: 'pointer',
722 handle: '.frm-move',
723 over: function() {
724 this.classList.add( 'drop-me' );
725 },
726 out: function() {
727 this.classList.remove( 'drop-me' );
728 },
729 receive: function( event, ui ) {
730 // Receive event occurs when an item in one sortable list is dragged into another sortable list
731
732 if ( cancelSort ) {
733 ui.item.addClass( 'frm_cancel_sort' );
734 return;
735 }
736
737 if ( typeof ui.item.attr( 'id' ) !== 'undefined' ) {
738 if ( ui.item.attr( 'id' ).indexOf( 'frm_field_id' ) > -1 ) {
739 // An existing field was dragged and dropped into, out of, or between sections
740 updateFieldAfterMovingBetweenSections( ui.item );
741 } else {
742 // A new field was dragged into the form
743 insertNewFieldByDragging( this, ui.item, opts );
744 }
745 }
746 },
747 change: function( event, ui ) {
748 // don't allow some field types inside section
749 if ( allowDrop( ui ) ) {
750 ui.placeholder.addClass( 'sortable-placeholder' ).removeClass( 'no-drop-placeholder' );
751 cancelSort = false;
752 } else {
753 ui.placeholder.addClass( 'no-drop-placeholder' ).removeClass( 'sortable-placeholder' );
754 cancelSort = true;
755 }
756 },
757 start: function( event, ui ) {
758 if ( ui.item[0].offsetHeight > 120 ) {
759 jQuery( sort ).sortable( 'refreshPositions' );
760 }
761 if ( ui.item[0].classList.contains( 'frm-page-collapsed' ) ) {
762 // If a page if collapsed, expand it before dragging since only the page break will move.
763 toggleCollapsePage( jQuery( ui.item[0]) );
764 }
765 },
766 helper: function( e, li ) {
767 copyHelper = li.clone().insertAfter( li );
768 return li.clone();
769 },
770 beforeStop: function( event, ui ) {
771 // If this was dropped at the beginning of a collpased page, open it.
772 var previous = ui.item[0].previousElementSibling;
773 if ( previous !== null && previous.classList.contains( 'frm-page-collapsed' ) ) {
774 toggleCollapsePage( jQuery( previous ) );
775 }
776 },
777 stop: function() {
778 var moving = jQuery( this );
779 copyHelper && copyHelper.remove();
780 if ( cancelSort ) {
781 moving.sortable( 'cancel' );
782 } else {
783 updateFieldOrder();
784 }
785 moving.children( '.edit_field_type_end_divider' ).appendTo( this );
786 },
787 sort: function( event ) {
788 container.scrollTop( function( i, v ) {
789 if ( startSort === false ) {
790 startSort = event.clientY;
791 return v;
792 }
793
794 var moved = event.clientY - startSort;
795 var h = this.offsetHeight;
796 var relativePos = event.clientY - this.offsetTop;
797 var y = relativePos - h / 2;
798 if ( relativePos > ( h - 50 ) && moved > 5 ) {
799 // scrolling down
800 return v + y * 0.1;
801 } else if ( relativePos < 50 && moved < -5 ) {
802 //scrolling up
803 return v - Math.abs( y * 0.1 );
804 }
805 });
806 }
807 };
808
809 jQuery( sort ).sortable( opts );
810
811 setupFieldOptionSorting( jQuery( '#frm_builder_page' ) );
812 }
813
814 function setupFieldOptionSorting( sort ) {
815 var opts = {
816 items: '.frm_sortable_field_opts li',
817 axis: 'y',
818 opacity: 0.65,
819 forcePlaceholderSize: false,
820 handle: '.frm-drag',
821 helper: function( e, li ) {
822 copyHelper = li.clone().insertAfter( li );
823 return li.clone();
824 },
825 stop: function( e, ui ) {
826 copyHelper && copyHelper.remove();
827 var fieldId = ui.item.attr( 'id' ).replace( 'frm_delete_field_', '' ).replace( '-' + ui.item.data( 'optkey' ) + '_container', '' );
828 resetDisplayedOpts( fieldId );
829 }
830 };
831
832 jQuery( sort ).sortable( opts );
833 }
834
835 // Get the section where a field is dropped
836 function getSectionForFieldPlacement( currentItem ) {
837 var section = '';
838 if ( typeof currentItem !== 'undefined' ) {
839 section = currentItem.closest( '.edit_field_type_divider' );
840 }
841
842 return section;
843 }
844
845 // Get the form ID where a field is dropped
846 function getFormIdForFieldPlacement( section ) {
847 var formId = '';
848
849 if ( typeof section[0] !== 'undefined' ) {
850 var sDivide = section.children( '.start_divider' );
851 sDivide.children( '.edit_field_type_end_divider' ).appendTo( sDivide );
852 if ( typeof section.attr( 'data-formid' ) !== 'undefined' ) {
853 var fieldId = section.attr( 'data-fid' );
854 formId = jQuery( 'input[name="field_options[form_select_' + fieldId + ']"]' ).val();
855 }
856 }
857
858 if ( typeof formId === 'undefined' || formId === '' ) {
859 formId = thisFormId;
860 }
861
862 return formId;
863 }
864
865 // Get the section ID where a field is dropped
866 function getSectionIdForFieldPlacement( section ) {
867 var sectionId = 0;
868 if ( typeof section[0] !== 'undefined' ) {
869 sectionId = section.attr( 'id' ).replace( 'frm_field_id_', '' );
870 }
871
872 return sectionId;
873 }
874
875 /**
876 * Update a field after it is dragged and dropped into, out of, or between sections
877 *
878 * @param {object} currentItem
879 */
880 function updateFieldAfterMovingBetweenSections( currentItem ) {
881 var fieldId = currentItem.attr( 'id' ).replace( 'frm_field_id_', '' );
882 var section = getSectionForFieldPlacement( currentItem );
883 var formId = getFormIdForFieldPlacement( section );
884 var sectionId = getSectionIdForFieldPlacement( section );
885
886 jQuery.ajax({
887 type: 'POST', url: ajaxurl,
888 data: {
889 action: 'frm_update_field_after_move',
890 form_id: formId,
891 field: fieldId,
892 section_id: sectionId,
893 nonce: frmGlobal.nonce
894 },
895 success: function() {
896 toggleSectionHolder();
897 updateInSectionValue( fieldId, sectionId );
898 }
899 });
900 }
901
902 // Update the in_section field value
903 function updateInSectionValue( fieldId, sectionId ) {
904 document.getElementById( 'frm_in_section_' + fieldId ).value = sectionId;
905 }
906
907 /**
908 * Add a new field by dragging and dropping it from the Fields sidebar
909 *
910 * @param {object} selectedItem
911 * @param {object} fieldButton
912 * @param {object} opts
913 */
914 function insertNewFieldByDragging( selectedItem, fieldButton ) {
915 var fieldType = fieldButton.attr( 'id' );
916
917 // We'll optimistically disable the button now. We'll re-enable if AJAX fails
918 if ( 'summary' === fieldType ) {
919 var addBtn = fieldButton.children( '.frm_add_field' );
920 disableSummaryBtnBeforeAJAX( addBtn, fieldButton );
921 }
922
923 var currentItem = jQuery( selectedItem ).data().uiSortable.currentItem;
924 var section = getSectionForFieldPlacement( currentItem );
925 var formId = getFormIdForFieldPlacement( section );
926 var sectionId = getSectionIdForFieldPlacement( section );
927
928 var loadingID = fieldType.replace( '|', '-' );
929 currentItem.replaceWith( '<li class="frm-wait frmbutton_loadingnow" id="' + loadingID + '" ></li>' );
930
931 var hasBreak = 0;
932 if ( 'summary' === fieldType ) {
933 // see if we need to insert a page break before this newly-added summary field. Check for at least 1 page break
934 hasBreak = jQuery( '.frmbutton_loadingnow#' + loadingID ).prevAll( 'li[data-type="break"]:first' ).length > 0 ? 1 : 0;
935 }
936
937 jQuery.ajax({
938 type: 'POST', url: ajaxurl,
939 data: {
940 action: 'frm_insert_field',
941 form_id: formId,
942 field_type: fieldType,
943 section_id: sectionId,
944 nonce: frmGlobal.nonce,
945 has_break: hasBreak
946 },
947 success: function( msg ) {
948 document.getElementById( 'frm_form_editor_container' ).classList.add( 'frm-has-fields' );
949 jQuery( '.frmbutton_loadingnow#' + loadingID ).replaceWith( msg );
950 updateFieldOrder();
951
952 afterAddField( msg, false );
953 },
954 error: function( jqXHR, textStatus, errorThrown ) {
955 maybeReenableSummaryBtnAfterAJAX( fieldType, addBtn, fieldButton, errorThrown );
956 }
957 });
958 }
959
960 // don't allow page break, embed form, captcha, summary, or section inside section field
961 function allowDrop( ui ) {
962 if ( ! ui.placeholder.parent().hasClass( 'start_divider' ) ) {
963 return true;
964 }
965
966 // new field
967 if ( ui.item.hasClass( 'frmbutton' ) ) {
968 if ( ui.item.hasClass( 'frm_tbreak' ) || ui.item.hasClass( 'frm_tform' ) || ui.item.hasClass( 'frm_tdivider' ) || ui.item.hasClass( 'frm_tdivider-repeat' ) ) {
969 return false;
970 }
971 return true;
972 }
973
974 // moving an existing field
975 return ! ( ui.item.hasClass( 'edit_field_type_break' ) || ui.item.hasClass( 'edit_field_type_form' ) ||
976 ui.item.hasClass( 'edit_field_type_divider' ) );
977 }
978
979 function loadFields( fieldId ) {
980 var $thisField = jQuery( document.getElementById( fieldId ) );
981 var fields;
982 if ( jQuery.isFunction( jQuery.fn.addBack ) ) {
983 fields = $thisField.nextAll( '*:lt(14)' ).addBack();
984 } else {
985 fields = $thisField.nextAll( '*:lt(14)' ).andSelf();
986 }
987 fields.addClass( 'frm_load_now' );
988
989 var h = [];
990 jQuery.each( fields, function( k, v ) {
991 h.push( jQuery( v ).find( '.frm_hidden_fdata' ).html() );
992 });
993
994 jQuery.ajax({
995 type: 'POST', url: ajaxurl,
996 data: {
997 action: 'frm_load_field',
998 field: h,
999 form_id: thisFormId,
1000 nonce: frmGlobal.nonce
1001 },
1002 success: function( html ) {
1003 html = html.replace( /^\s+|\s+$/g, '' );
1004 if ( html.indexOf( '{' ) !== 0 ) {
1005 jQuery( '.frm_load_now' ).removeClass( '.frm_load_now' ).html( 'Error' );
1006 return;
1007 }
1008 html = jQuery.parseJSON( html );
1009
1010 for ( var key in html ) {
1011 jQuery( '#frm_field_id_' + key ).replaceWith( html[key]);
1012 setupSortable( '#frm_field_id_' + key + '.edit_field_type_divider ul.frm_sorting' );
1013 }
1014
1015 var $nextSet = $thisField.nextAll( '.frm_field_loading:not(.frm_load_now)' );
1016 if ( $nextSet.length ) {
1017 loadFields( $nextSet.attr( 'id' ) );
1018 } else {
1019 // go up a level
1020 $nextSet = jQuery( document.getElementById( 'frm-show-fields' ) ).find( '.frm_field_loading:not(.frm_load_now)' );
1021 if ( $nextSet.length ) {
1022 loadFields( $nextSet.attr( 'id' ) );
1023 }
1024 }
1025
1026 initiateMultiselect();
1027 renumberPageBreaks();
1028 maybeHideQuantityProductFieldOption();
1029 }
1030 });
1031 }
1032
1033 function addFieldClick() {
1034 /*jshint validthis:true */
1035 var $thisObj = jQuery( this );
1036 // there is no real way to disable a <a> (with a valid href attribute) in HTML - https://css-tricks.com/how-to-disable-links/
1037 if ( $thisObj.hasClass( 'disabled' ) ) {
1038 return false;
1039 }
1040
1041 var $button = $thisObj.closest( '.frmbutton' );
1042 var fieldType = $button.attr( 'id' );
1043
1044 var hasBreak = 0;
1045 if ( 'summary' === fieldType ) {
1046 // We'll optimistically disable $button now. We'll re-enable if AJAX fails
1047 disableSummaryBtnBeforeAJAX( $thisObj, $button );
1048
1049 hasBreak = $newFields.children( 'li[data-type="break"]' ).length > 0 ? 1 : 0;
1050 }
1051
1052 var formId = thisFormId;
1053
1054 jQuery.ajax({
1055 type: 'POST',
1056 url: ajaxurl,
1057 data: {
1058 action: 'frm_insert_field',
1059 form_id: formId,
1060 field_type: fieldType,
1061 section_id: 0,
1062 nonce: frmGlobal.nonce,
1063 has_break: hasBreak
1064 },
1065 success: function( msg ) {
1066 document.getElementById( 'frm_form_editor_container' ).classList.add( 'frm-has-fields' );
1067 $newFields.append( msg );
1068 afterAddField( msg, true );
1069 },
1070 error: function( jqXHR, textStatus, errorThrown ) {
1071 maybeReenableSummaryBtnAfterAJAX( fieldType, $thisObj, $button, errorThrown );
1072 }
1073 });
1074 return false;
1075 }
1076
1077 function disableSummaryBtnBeforeAJAX( addBtn, fieldButton ) {
1078 addBtn.addClass( 'disabled' );
1079 fieldButton.draggable( 'disable' );
1080 }
1081
1082 function reenableAddSummaryBtn() {
1083 var frmBtn = jQuery( 'li#summary' );
1084 var addFieldLink = frmBtn.children( '.frm_add_field' );
1085 frmBtn.draggable( 'enable' );
1086 addFieldLink.removeClass( 'disabled' );
1087 }
1088
1089 function maybeDisableAddSummaryBtn() {
1090 if ( formHasSummaryField() ) {
1091 disableAddSummaryBtn();
1092 }
1093 }
1094
1095 function disableAddSummaryBtn() {
1096 var frmBtn = jQuery( 'li#summary' );
1097 var addFieldLink = frmBtn.children( '.frm_add_field' );
1098 frmBtn.draggable( 'disable' );
1099 addFieldLink.addClass( 'disabled' );
1100 }
1101
1102 function maybeReenableSummaryBtnAfterAJAX( fieldType, addBtn, fieldButton, errorThrown ) {
1103 infoModal( errorThrown + '. Please try again.' );
1104 if ( 'summary' === fieldType ) {
1105 addBtn.removeClass( 'disabled' );
1106 fieldButton.draggable( 'enable' );
1107 }
1108 }
1109
1110 function formHasSummaryField() {
1111 // .edit_field_type_summary is a better selector here in order to also cover fields loaded by AJAX
1112 return $newFields.children( 'li.edit_field_type_summary' ).length > 0;
1113 }
1114
1115 function maybeHideQuantityProductFieldOption() {
1116 var hide = true,
1117 opts = document.querySelectorAll( '.frmjs_prod_field_opt_cont' );
1118
1119 if ( $newFields.find( 'li.edit_field_type_product' ).length > 1 ) {
1120 hide = false;
1121 }
1122
1123 for ( var i = 0; i < opts.length; i++ ) {
1124 if ( hide ) {
1125 opts[ i ].classList.add( 'frm_hidden' );
1126 } else {
1127 opts[ i ].classList.remove( 'frm_hidden' );
1128 }
1129 }
1130 }
1131
1132 function duplicateField() {
1133 /*jshint validthis:true */
1134 var thisField = jQuery( this ).closest( 'li' );
1135 var fieldId = thisField.data( 'fid' );
1136 var children = fieldsInSection( fieldId );
1137
1138 if ( thisField.hasClass( 'frm-section-collapsed' ) || thisField.hasClass( 'frm-page-collapsed' ) ) {
1139 return false;
1140 }
1141
1142 jQuery.ajax({
1143 type: 'POST',
1144 url: ajaxurl,
1145 data: {
1146 action: 'frm_duplicate_field',
1147 field_id: fieldId,
1148 form_id: thisFormId,
1149 children: children,
1150 nonce: frmGlobal.nonce
1151 },
1152 success: function( msg ) {
1153 thisField.after( msg );
1154 updateFieldOrder();
1155 afterAddField( msg, false );
1156 }
1157 });
1158 return false;
1159 }
1160
1161 function afterAddField( msg, addFocus ) {
1162 var regex = /id="(\S+)"/,
1163 match = regex.exec( msg ),
1164 field = document.getElementById( match[1]),
1165 section = '#' + match[1] + '.edit_field_type_divider ul.frm_sorting',
1166 $thisSection = jQuery( section ),
1167 type = field.getAttribute( 'data-type' ),
1168 toggled = false;
1169
1170 setupSortable( section );
1171
1172 if ( 'quantity' === type ) {
1173 // try to automatically attach a product field
1174 maybeSetProductField( field );
1175 }
1176
1177 if ( 'product' === type || 'quantity' === type ) {
1178 // quantity too needs to be a part of the if stmt especially cos of the very
1179 // 1st quantity field (or even if it's just one quantity field in the form).
1180 maybeHideQuantityProductFieldOption();
1181 }
1182
1183 if ( $thisSection.length ) {
1184 $thisSection.parent( '.frm_field_box' ).children( '.frm_no_section_fields' ).addClass( 'frm_block' );
1185 } else {
1186 var $parentSection = jQuery( field ).closest( 'ul.frm_sorting' );
1187 if ( $parentSection.length ) {
1188 toggleOneSectionHolder( $parentSection );
1189 toggled = true;
1190 }
1191 }
1192
1193 if ( msg.indexOf( 'frm-collapse-page' ) !== -1 ) {
1194 renumberPageBreaks();
1195 }
1196
1197 addClass( field, 'frm-newly-added' );
1198 setTimeout( function() {
1199 field.classList.remove( 'frm-newly-added' );
1200 }, 1000 );
1201
1202 if ( addFocus ) {
1203 var bounding = field.getBoundingClientRect(),
1204 container = document.getElementById( 'post-body-content' ),
1205 inView = ( bounding.top >= 0 &&
1206 bounding.left >= 0 &&
1207 bounding.right <= ( window.innerWidth || document.documentElement.clientWidth ) &&
1208 bounding.bottom <= ( window.innerHeight || document.documentElement.clientHeight )
1209 );
1210
1211 if ( ! inView ) {
1212 container.scroll({
1213 top: container.scrollHeight,
1214 left: 0,
1215 behavior: 'smooth'
1216 });
1217 }
1218
1219 if ( toggled === false ) {
1220 toggleOneSectionHolder( $thisSection );
1221 }
1222 }
1223
1224 deselectFields();
1225 initiateMultiselect();
1226 }
1227
1228 function clearSettingsBox() {
1229 jQuery( '#new_fields .frm-single-settings' ).addClass( 'frm_hidden' );
1230 jQuery( '#frm-options-panel > .frm-single-settings' ).removeClass( 'frm_hidden' );
1231 deselectFields();
1232 }
1233
1234 function deselectFields() {
1235 jQuery( 'li.ui-state-default.selected' ).removeClass( 'selected' );
1236 }
1237
1238 function scrollToField( field ) {
1239 var newPos = field.getBoundingClientRect().top,
1240 container = document.getElementById( 'post-body-content' );
1241
1242 if ( typeof animate === 'undefined' ) {
1243 jQuery( container ).scrollTop( newPos );
1244 } else {
1245 // TODO: smooth scroll
1246 jQuery( container ).animate({ scrollTop: newPos }, 500 );
1247 }
1248 }
1249
1250 function checkCalculationCreatedByUser() {
1251 var calculation = this.value;
1252 var warningMessage = checkMatchingParens( calculation );
1253 warningMessage += checkShortcodes( calculation, this );
1254
1255 if ( warningMessage !== '' ) {
1256 infoModal( calculation + '\n\n' + warningMessage );
1257 }
1258 }
1259
1260 /**
1261 * Checks the Detail Page slug to see if it's a reserved word and displays a message if it is.
1262 */
1263 function checkDetailPageSlug() {
1264 var slug = jQuery( '#param' ).val(),
1265 msg;
1266 slug = slug.trim().toLowerCase();
1267 if ( Array.isArray( frm_admin_js.unsafe_params ) && frm_admin_js.unsafe_params.includes( slug ) ) {
1268 msg = frm_admin_js.slug_is_reserved;
1269 msg = msg.replace( '****', addHtmlTags( slug, 'strong' ) );
1270 msg += '<br /><br />';
1271 msg += addHtmlTags( '<a href="https://codex.wordpress.org/WordPress_Query_Vars" target="_blank" class="frm-standard-link">' + frm_admin_js.reserved_words + '</a>', 'div' );
1272 infoModal( msg );
1273 }
1274 }
1275
1276 /**
1277 * Checks View filter value for params named with reserved words and displays a message if any are found.
1278 */
1279 function checkFilterParamNames() {
1280 var regEx = /\[\s*get\s*param\s*=\s*['"]?([a-zA-Z-_]+)['"]?/ig,
1281 filterValue = jQuery( this ).val(),
1282 match = regEx.exec( filterValue ),
1283 unsafeParams = '';
1284
1285 while ( match !== null ) {
1286 if ( Array.isArray( frm_admin_js.unsafe_params ) && frm_admin_js.unsafe_params.includes( match[1]) ) {
1287 if ( unsafeParams !== '' ) {
1288 unsafeParams += '", "' + match[ 1 ];
1289 } else {
1290 unsafeParams = match[ 1 ];
1291 }
1292 }
1293 match = regEx.exec( filterValue );
1294 }
1295
1296 if ( unsafeParams !== '' ) {
1297 msg = frm_admin_js.param_is_reserved;
1298 msg = msg.replace( '****', addHtmlTags( unsafeParams, 'strong' ) );
1299 msg += '<br /><br />';
1300 msg += ' <a href="https://codex.wordpress.org/WordPress_Query_Vars" target="_blank" class="frm-standard-link">' + frm_admin_js.reserved_words + '</a>';
1301
1302 infoModal( msg );
1303 }
1304 }
1305
1306 function addHtmlTags( text, tag ) {
1307 tag = tag ? tag : 'p';
1308 return '<' + tag + '>' + text + '</' + tag + '>';
1309 }
1310
1311 /**
1312 * Checks a string for parens, brackets, and curly braces and returns a message if any unmatched are found.
1313 * @param formula
1314 * @returns {string}
1315 */
1316 function checkMatchingParens( formula ) {
1317
1318 var stack = [],
1319 formulaArray = formula.split( '' ),
1320 length = formulaArray.length,
1321 opening = [ '{', '[', '(' ],
1322 closing = {
1323 '}': '{',
1324 ')': '(',
1325 ']': '['
1326 },
1327 unmatchedClosing = [],
1328 msg = '',
1329 i, top;
1330
1331 for ( i = 0; i < length; i++ ) {
1332 if ( opening.includes( formulaArray[i]) ) {
1333 stack.push( formulaArray[i]);
1334 continue;
1335 }
1336 if ( closing.hasOwnProperty( formulaArray[i]) ) {
1337 top = stack.pop();
1338 if ( top !== closing[formulaArray[i]]) {
1339 unmatchedClosing.push( formulaArray[i]);
1340 }
1341 }
1342 }
1343
1344 if ( stack.length > 0 || unmatchedClosing.length > 0 ) {
1345 msg = frm_admin_js.unmatched_parens + '\n\n';
1346 return msg;
1347 }
1348
1349 return '';
1350 }
1351
1352 /**
1353 * Checks a calculation for shortcodes that shouldn't be in it and returns a message if found.
1354 * @param calculation
1355 * @param inputElement
1356 * @returns {string}
1357 */
1358 function checkShortcodes( calculation, inputElement ) {
1359 var msg = checkNonNumericShortcodes( calculation, inputElement );
1360 msg += checkNonFormShortcodes( calculation );
1361
1362 return msg;
1363 }
1364
1365 /**
1366 * Checks if a numeric calculation has shortcodes that output non-numeric strings and returns a message if found.
1367 * @param calculation
1368 *
1369 * @param inputElement
1370 * @returns {string}
1371 */
1372 function checkNonNumericShortcodes( calculation, inputElement ) {
1373
1374 var msg = '';
1375
1376 if ( isTextCalculation( inputElement ) ) {
1377 return msg;
1378 }
1379
1380 var nonNumericShortcodes = getNonNumericShortcodes();
1381
1382 if ( nonNumericShortcodes.test( calculation ) ) {
1383 msg = frm_admin_js.text_shortcodes + '\n\n';
1384 }
1385
1386 return msg;
1387 }
1388
1389 /**
1390 * Determines if the calculation input is from a text calculation.
1391 *
1392 * @param inputElement
1393 */
1394 function isTextCalculation( inputElement ) {
1395 return jQuery( inputElement ).siblings( 'label[for^="calc_type"]' ).children( 'input' ).prop( 'checked' );
1396 }
1397
1398 /**
1399 * Returns a regular expression of shortcodes that can't be used in numeric calculations.
1400 * @returns {RegExp}
1401 */
1402 function getNonNumericShortcodes() {
1403 return /\[(date|time|email|ip)\]/;
1404 }
1405
1406 /**
1407 * Checks if a string has any shortcodes that do not belong in forms and returns a message if any are found.
1408 * @param formula
1409 * @returns {string}
1410 */
1411 function checkNonFormShortcodes( formula ) {
1412 var nonFormShortcodes = getNonFormShortcodes(),
1413 msg = '';
1414
1415 if ( nonFormShortcodes.test( formula ) ) {
1416 msg += frm_admin_js.view_shortcodes + '\n\n';
1417 }
1418
1419 return msg;
1420 }
1421
1422 /**
1423 * Returns a regular expression of shortcodes that can't be used in forms but can be used in Views, Email
1424 * Notifications, and other Formidable areas.
1425 *
1426 * @returns {RegExp}
1427 */
1428 function getNonFormShortcodes() {
1429 return /\[id\]|\[key\]|\[if\s\w+\]|\[foreach\s\w+\]|\[created-at(\s*)?/g;
1430 }
1431
1432 function isCalcBoxType( box, listClass ) {
1433 var list = jQuery( box ).find( '.frm_code_list' );
1434 return 1 === list.length && list.hasClass( listClass );
1435 }
1436
1437 function extractExcludedOptions( exclude ) {
1438 var opts = [];
1439 if ( ! Array.isArray( exclude ) ) {
1440 return opts;
1441 }
1442
1443 for ( var i = 0; i < exclude.length; i++ ) {
1444 if ( exclude[ i ].startsWith( '[' ) ) {
1445 opts.push( exclude[ i ]);
1446 // remove it
1447 exclude.splice( i, 1 );
1448 // https://love2dev.com/blog/javascript-remove-from-array/#remove-from-array-splice-value
1449 i--;
1450 }
1451 }
1452
1453 return opts;
1454 }
1455
1456 function hasExcludedOption( field, excludedOpts ) {
1457 var hasOption = false;
1458 for ( var i = 0; i < excludedOpts.length; i++ ) {
1459 var inputs = document.getElementsByName( getFieldOptionInputName( excludedOpts[ i ], field.fieldId ) );
1460 // 2nd condition checks that there's at least one non-empty value
1461 if ( inputs.length && jQuery( inputs[0]).val() ) {
1462 hasOption = true;
1463 break;
1464 }
1465 }
1466 return hasOption;
1467 }
1468
1469 function getFieldOptionInputName( opt, fieldId ) {
1470 var at = opt.indexOf( ']' );
1471 return 'field_options' + opt.substring( 0, at ) + '_' + fieldId + opt.substring( at );
1472 }
1473
1474 function popCalcFields( v, force ) {
1475 var box, exclude, fields, i, list,
1476 p = jQuery( v ).closest( '.frm-single-settings' ),
1477 calc = p.find( '.frm-calc-field' );
1478
1479 if ( ! force && ( ! calc.length || calc.val() === '' || calc.is( ':hidden' ) ) ) {
1480 return;
1481 }
1482
1483 var isSummary = isCalcBoxType( v, 'frm_js_summary_list' );
1484
1485 var fieldId = p.find( 'input[name="frm_fields_submitted[]"]' ).val();
1486
1487 if ( force ) {
1488 box = v;
1489 } else {
1490 box = document.getElementById( 'frm-calc-box-' + fieldId );
1491 }
1492
1493 exclude = getExcludeArray( box, isSummary );
1494 var excludedOpts = extractExcludedOptions( exclude );
1495
1496 fields = getFieldList();
1497 list = document.getElementById( 'frm-calc-list-' + fieldId );
1498 list.innerHTML = '';
1499
1500 for ( i = 0; i < fields.length; i++ ) {
1501 if ( ( exclude && exclude.includes( fields[ i ].fieldType ) ) ||
1502 ( excludedOpts.length && hasExcludedOption( fields[ i ], excludedOpts ) ) ) {
1503 continue;
1504 }
1505
1506 var span = document.createElement( 'span' );
1507 span.appendChild( document.createTextNode( '[' + fields[i].fieldId + ']' ) );
1508
1509 var a = document.createElement( 'a' );
1510 a.setAttribute( 'href', '#' );
1511 a.setAttribute( 'data-code', fields[i].fieldId );
1512 a.classList.add( 'frm_insert_code' );
1513 a.appendChild( span );
1514 a.appendChild( document.createTextNode( fields[i].fieldName ) );
1515
1516 var li = document.createElement( 'li' );
1517 li.classList.add( 'frm-field-list-' + fieldId );
1518 li.classList.add( 'frm-field-list-' + fields[i].fieldType );
1519 li.appendChild( a );
1520 list.appendChild( li );
1521 }
1522 }
1523
1524 function getExcludeArray( calcBox, isSummary ) {
1525 var exclude = JSON.parse( calcBox.getElementsByClassName( 'frm_code_list' )[0].getAttribute( 'data-exclude' ) );
1526
1527 if ( isSummary ) {
1528 // includedExtras are those that are normally excluded from the summary but the form owner can choose to include,
1529 // when they have been chosen to be included, then they can now be manually excluded in the calc box.
1530 var includedExtras = getIncludedExtras();
1531 if ( includedExtras.length ) {
1532 for ( var i = 0; i < exclude.length; i++ ) {
1533 if ( includedExtras.includes( exclude[ i ]) ) {
1534 // remove it
1535 exclude.splice( i, 1 );
1536 // https://love2dev.com/blog/javascript-remove-from-array/#remove-from-array-splice-value
1537 i--;
1538 }
1539 }
1540 }
1541 }
1542
1543 return exclude;
1544 }
1545
1546 function getIncludedExtras() {
1547 var checked = [];
1548 var checkboxes = document.getElementsByClassName( 'frm_include_extras_field' );
1549
1550 for ( var i = 0; i < checkboxes.length; i++ ) {
1551 if ( checkboxes[i].checked ) {
1552 checked.push( checkboxes[i].value );
1553 }
1554 }
1555
1556 return checked;
1557 }
1558
1559 function rePopCalcFieldsForSummary() {
1560 popCalcFields( jQuery( '.frm-inline-modal.postbox:has(.frm_js_summary_list)' )[0], true );
1561 }
1562
1563 function getFieldList( fieldType ) {
1564 var i,
1565 fields = [],
1566 allFields = document.querySelectorAll( 'li.frm_field_box' ),
1567 checkType = 'undefined' !== typeof fieldType;
1568
1569 for ( i = 0; i < allFields.length; i++ ) {
1570 // data-ftype is better (than data-type) cos of fields loaded by AJAX - which might not be ready yet
1571 if ( checkType && allFields[ i ].getAttribute( 'data-ftype' ) !== fieldType ) {
1572 continue;
1573 }
1574
1575 var fieldId = allFields[ i ].getAttribute( 'data-fid' );
1576 if ( typeof fieldId !== 'undefined' && fieldId ) {
1577 fields.push({
1578 'fieldId': fieldId,
1579 'fieldName': getPossibleValue( 'frm_name_' + fieldId ),
1580 'fieldType': getPossibleValue( 'field_options_type_' + fieldId ),
1581 'fieldKey': getPossibleValue( 'field_options_field_key_' + fieldId )
1582 });
1583 }
1584 }
1585
1586 return fields;
1587 }
1588
1589 function popProductFields( field ) {
1590 var i, checked, id,
1591 options = [],
1592 current = getCurrentProductFields( field ),
1593 fName = field.getAttribute( 'data-frmfname' ),
1594 products = getFieldList( 'product' ),
1595 quantities = getFieldList( 'quantity' ),
1596 isSelect = field.tagName === 'SELECT', // for reverse compatibility.
1597 // whether we have just 1 product and 1 quantity field & should therefore attach the latter to the former
1598 auto = 1 === quantities.length && 1 === products.length;
1599
1600 if ( isSelect ) {
1601 // This fallback can be removed after 4.05.
1602 current = field.getAttribute( 'data-frmcurrent' );
1603 }
1604
1605 for ( i = 0 ; i < products.length ; i++ ) {
1606 // let's be double sure it's string, else indexOf will fail
1607 id = products[ i ].fieldId.toString();
1608 checked = auto || -1 !== current.indexOf( id );
1609 if ( isSelect ) {
1610 // This fallback can be removed after 4.05.
1611 checked = checked ? ' selected' : '';
1612 options.push( '<option value="' + id + '"' + checked + '>' + products[ i ].fieldName + '</option>' );
1613 } else {
1614 checked = checked ? ' checked' : '';
1615 options.push( '<label class="frm6">' );
1616 options.push( '<input type="checkbox" name="' + fName + '" value="' + id + '"' + checked + '> ' + products[ i ].fieldName );
1617 options.push( '</label>' );
1618 }
1619 }
1620
1621 field.innerHTML = options.join( '' );
1622 }
1623
1624 function getCurrentProductFields( prodFieldOpt ) {
1625 var products = prodFieldOpt.querySelectorAll( '[type="checkbox"]:checked' ),
1626 idsArray = [];
1627
1628 for ( var i = 0; i < products.length; i++ ) {
1629 idsArray.push( products[ i ].value );
1630 }
1631
1632 return idsArray;
1633 }
1634
1635 function popAllProductFields() {
1636 var opts = document.querySelectorAll( '.frmjs_prod_field_opt' );
1637 for ( var i = 0; i < opts.length; i++ ) {
1638 popProductFields( opts[ i ]);
1639 }
1640 }
1641
1642 function maybeSetProductField( field ) {
1643 var fieldId = field.getAttribute( 'data-fid' ),
1644 productFieldOpt = document.getElementById( 'field_options[product_field_' + fieldId + ']' );
1645
1646 if ( null === productFieldOpt ) {
1647 return;
1648 }
1649
1650 popProductFields( productFieldOpt );
1651 // in order to move its settings to that LHS panel where
1652 // the update form resides, else it'll lose this setting
1653 moveFieldSettings( document.getElementById( 'frm-single-settings-' + fieldId ) );
1654 }
1655
1656 /**
1657 * If the element doesn't exist, use a blank value.
1658 */
1659 function getPossibleValue( id ) {
1660 field = document.getElementById( id );
1661 if ( field !== null ) {
1662 return field.value;
1663 } else {
1664 return '';
1665 }
1666 }
1667
1668 function liveChanges() {
1669 /*jshint validthis:true */
1670 var option,
1671 newValue = this.value,
1672 changes = document.getElementById( this.getAttribute( 'data-changeme' ) ),
1673 att = this.getAttribute( 'data-changeatt' );
1674
1675 if ( changes === null ) {
1676 return;
1677 }
1678
1679 if ( att !== null ) {
1680 if ( changes.tagName === 'SELECT' && att === 'placeholder' ) {
1681 option = changes.options[0];
1682 if ( option.value === '' ) {
1683 option.innerHTML = newValue;
1684 } else {
1685 // Create a placeholder option if there are no blank values.
1686 addBlankSelectOption( changes, newValue );
1687 }
1688 } else if ( att === 'class' ) {
1689 changeFieldClass( changes, this );
1690 } else {
1691 changes.setAttribute( att, newValue );
1692 }
1693 } else if ( changes.id.indexOf( 'setup-message' ) === 0 ) {
1694 if ( newValue !== '' ) {
1695 changes.innerHTML = '<input type="text" value="" disabled />';
1696 }
1697 } else {
1698 changes.innerHTML = newValue;
1699 }
1700 }
1701
1702 function toggleInvalidMsg() {
1703 /*jshint validthis:true */
1704 var typeDropdown, fieldType,
1705 fieldId = this.id.replace( 'frm_format_', '' ),
1706 hasValue = this.value !== '';
1707
1708 typeDropdown = document.getElementsByName( 'field_options[type_' + fieldId + ']' )[0];
1709 fieldType = typeDropdown.options[typeDropdown.selectedIndex].value;
1710
1711 if ( fieldType === 'text' ) {
1712 toggleValidationBox( hasValue, '.frm_invalid_msg' + fieldId );
1713 }
1714 }
1715
1716 function markRequired() {
1717 /*jshint validthis:true */
1718 var thisid = this.id.replace( 'frm_', '' ),
1719 fieldId = thisid.replace( 'req_field_', '' ),
1720 checked = this.checked,
1721 label = jQuery( '#field_label_' + fieldId + ' .frm_required' );
1722
1723 toggleValidationBox( checked, '.frm_required_details' + fieldId );
1724
1725 if ( checked ) {
1726 var $reqBox = jQuery( 'input[name="field_options[required_indicator_' + fieldId + ']"]' );
1727 if ( $reqBox.val() === '' ) {
1728 $reqBox.val( '*' );
1729 }
1730 label.removeClass( 'frm_hidden' );
1731 } else {
1732 label.addClass( 'frm_hidden' );
1733 }
1734 }
1735
1736 function toggleValidationBox( hasValue, messageClass ) {
1737 $msg = jQuery( messageClass );
1738 if ( hasValue ) {
1739 $msg.fadeIn( 'fast' ).closest( '.frm_validation_msg' ).fadeIn( 'fast' );
1740 } else {
1741 //Fade out validation options
1742 var v = $msg.fadeOut( 'fast' ).closest( '.frm_validation_box' ).children( ':not(' + messageClass + '):visible' ).length;
1743 if ( v === 0 ) {
1744 $msg.closest( '.frm_validation_msg' ).fadeOut( 'fast' );
1745 }
1746 }
1747 }
1748
1749 function markUnique() {
1750 /*jshint validthis:true */
1751 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
1752 var $thisField = jQuery( '.frm_unique_details' + fieldId );
1753 if ( this.checked ) {
1754 $thisField.fadeIn( 'fast' ).closest( '.frm_validation_msg' ).fadeIn( 'fast' );
1755 $unqDetail = jQuery( '.frm_unique_details' + fieldId + ' input' );
1756 if ( $unqDetail.val() === '' ) {
1757 $unqDetail.val( frm_admin_js.default_unique );
1758 }
1759 } else {
1760 var v = $thisField.fadeOut( 'fast' ).closest( '.frm_validation_box' ).children( ':not(.frm_unique_details' + fieldId + '):visible' ).length;
1761 if ( v === 0 ) {
1762 $thisField.closest( '.frm_validation_msg' ).fadeOut( 'fast' );
1763 }
1764 }
1765 }
1766
1767 //Fade confirmation field and validation option in or out
1768 function addConf() {
1769 /*jshint validthis:true */
1770 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
1771 var val = jQuery( this ).val();
1772 var $thisField = jQuery( document.getElementById( 'frm_field_id_' + fieldId ) );
1773
1774 toggleValidationBox( val !== '', '.frm_conf_details' + fieldId );
1775
1776 if ( val !== '' ) {
1777 //Add default validation message if empty
1778 var valMsg = jQuery( '.frm_validation_box .frm_conf_details' + fieldId + ' input' );
1779 if ( valMsg.val() === '' ) {
1780 valMsg.val( frm_admin_js.default_conf );
1781 }
1782
1783 setConfirmationFieldDescriptions( fieldId );
1784
1785 //Add or remove class for confirmation field styling
1786 if ( val === 'inline' ) {
1787 $thisField.removeClass( 'frm_conf_below' ).addClass( 'frm_conf_inline' );
1788 } else if ( val === 'below' ) {
1789 $thisField.removeClass( 'frm_conf_inline' ).addClass( 'frm_conf_below' );
1790 }
1791 jQuery( '.frm-conf-box-' + fieldId ).removeClass( 'frm_hidden' );
1792 } else {
1793 jQuery( '.frm-conf-box-' + fieldId ).addClass( 'frm_hidden' );
1794 setTimeout( function() {
1795 $thisField.removeClass( 'frm_conf_inline frm_conf_below' );
1796 }, 200 );
1797 }
1798 }
1799
1800 function setConfirmationFieldDescriptions( fieldId ) {
1801 var fieldType = document.getElementsByName( 'field_options[type_' + fieldId + ']' )[0].value;
1802
1803 var fieldDescription = document.getElementById( 'field_description_' + fieldId );
1804 var hiddenDescName = 'field_options[description_' + fieldId + ']';
1805 var newValue = frm_admin_js['enter_' + fieldType];
1806 maybeSetNewDescription( fieldDescription, hiddenDescName, newValue );
1807
1808 var confFieldDescription = document.getElementById( 'conf_field_description_' + fieldId );
1809 var hiddenConfName = 'field_options[conf_desc_' + fieldId + ']';
1810 var newConfValue = frm_admin_js['confirm_' + fieldType];
1811 maybeSetNewDescription( confFieldDescription, hiddenConfName, newConfValue );
1812 }
1813
1814 function maybeSetNewDescription( descriptionDiv, hiddenName, newValue ) {
1815 if ( descriptionDiv.innerHTML === frm_admin_js.desc ) {
1816
1817 // Set the visible description value and the hidden description value
1818 descriptionDiv.innerHTML = newValue;
1819 document.getElementsByName( hiddenName )[0].value = newValue;
1820 }
1821 }
1822
1823 function initBulkOptionsOverlay() {
1824 /*jshint validthis:true */
1825 var $info = initModal( '#frm-bulk-modal', '700px' );
1826 if ( $info === false ) {
1827 return;
1828 }
1829
1830 jQuery( '.frm-insert-preset' ).click( insertBulkPreset );
1831
1832 jQuery( builderForm ).on( 'click', 'a.frm-bulk-edit-link', function( event ) {
1833 event.preventDefault();
1834 var i, key, label,
1835 content = '',
1836 fieldId = jQuery( this ).closest( '[data-fid]' ).data( 'fid' ),
1837 separate = usingSeparateValues( fieldId ),
1838 optList = document.getElementById( 'frm_field_' + fieldId + '_opts' ),
1839 opts = optList.getElementsByTagName( 'li' ),
1840 product = isProductField( fieldId );
1841
1842 document.getElementById( 'bulk-field-id' ).value = fieldId;
1843
1844 for ( i = 0; i < opts.length; i++ ) {
1845 key = opts[i].getAttribute( 'data-optkey' );
1846 if ( key !== '000' ) {
1847 label = document.getElementsByName( 'field_options[options_' + fieldId + '][' + key + '][label]' )[0];
1848 if ( typeof label !== 'undefined' ) {
1849 content += label.value;
1850 if ( separate ) {
1851 content += '|' + document.getElementsByName( 'field_options[options_' + fieldId + '][' + key + '][value]' )[0].value;
1852 }
1853 if ( product ) {
1854 content += '|' + document.getElementsByName( 'field_options[options_' + fieldId + '][' + key + '][price]' )[0].value;
1855 }
1856 content += '\r\n';
1857 }
1858 }
1859
1860 if ( i >= opts.length - 1 ) {
1861 document.getElementById( 'frm_bulk_options' ).value = content;
1862 }
1863 }
1864
1865 $info.dialog( 'open' );
1866
1867 return false;
1868 });
1869
1870 jQuery( '#frm-update-bulk-opts' ).click( function() {
1871 var fieldId = document.getElementById( 'bulk-field-id' ).value;
1872 this.classList.add( 'frm_loading_button' );
1873 frmAdminBuild.updateOpts( fieldId, document.getElementById( 'frm_bulk_options' ).value, $info );
1874 });
1875 }
1876
1877 function insertBulkPreset( event ) {
1878 /*jshint validthis:true */
1879 var opts = JSON.parse( this.getAttribute( 'data-opts' ) );
1880 event.preventDefault();
1881 document.getElementById( 'frm_bulk_options' ).value = opts.join( '\n' );
1882 return false;
1883 }
1884
1885 //Add new option or "Other" option to radio/checkbox/dropdown
1886 function addFieldOption() {
1887 /*jshint validthis:true */
1888 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' ),
1889 newOption = jQuery( '#frm_field_' + fieldId + '_opts .frm_option_template' ).prop( 'outerHTML' ),
1890 optType = jQuery( this ).data( 'opttype' ),
1891 optKey = 0,
1892 oldKey = '000',
1893 lastKey = getHighestOptKey( fieldId );
1894
1895 if ( lastKey !== oldKey ) {
1896 optKey = lastKey + 1;
1897 }
1898
1899 //Update hidden field
1900 if ( optType === 'other' ) {
1901 document.getElementById( 'other_input_' + fieldId ).value = 1;
1902
1903 //Hide "Add Other" option now if this is radio field
1904 var ftype = jQuery( this ).data( 'ftype' );
1905 if ( ftype === 'radio' || ftype === 'select' ) {
1906 jQuery( this ).fadeOut( 'slow' );
1907 }
1908
1909 var data = {
1910 action: 'frm_add_field_option',
1911 field_id: fieldId,
1912 opt_key: optKey,
1913 opt_type: optType,
1914 nonce: frmGlobal.nonce
1915 };
1916 jQuery.post( ajaxurl, data, function( msg ) {
1917 jQuery( document.getElementById( 'frm_field_' + fieldId + '_opts' ) ).append( msg );
1918 resetDisplayedOpts( fieldId );
1919 });
1920 } else {
1921 newOption = newOption.replace( new RegExp( 'optkey="' + oldKey + '"', 'g' ), 'optkey="' + optKey + '"' );
1922 newOption = newOption.replace( new RegExp( '-' + oldKey + '_', 'g' ), '-' + optKey + '_' );
1923 newOption = newOption.replace( new RegExp( '-' + oldKey + '"', 'g' ), '-' + optKey + '"' );
1924 newOption = newOption.replace( new RegExp( '\\[' + oldKey + '\\]', 'g' ), '[' + optKey + ']' );
1925 newOption = newOption.replace( 'frm_hidden frm_option_template', '' );
1926 jQuery( document.getElementById( 'frm_field_' + fieldId + '_opts' ) ).append( newOption );
1927 resetDisplayedOpts( fieldId );
1928 }
1929 }
1930
1931 function getHighestOptKey( fieldId ) {
1932 var i = 0,
1933 optKey = 0,
1934 opts = jQuery( '#frm_field_' + fieldId + '_opts li' ),
1935 lastKey = 0;
1936
1937 for ( i; i < opts.length; i++ ) {
1938 optKey = opts[i].getAttribute( 'data-optkey' );
1939 if ( opts.length === 1 ) {
1940 return optKey;
1941 }
1942 if ( optKey !== '000' ) {
1943 optKey = optKey.replace( 'other_', '' );
1944 optKey = parseInt( optKey, 10 );
1945 }
1946
1947 if ( ! isNaN( lastKey ) && ( optKey > lastKey || lastKey === '000' ) ) {
1948 lastKey = optKey;
1949 }
1950 }
1951
1952 return lastKey;
1953 }
1954
1955 function toggleMultSel() {
1956 /*jshint validthis:true */
1957 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
1958 toggleMultiSelect( fieldId, this.value );
1959 }
1960
1961 function toggleMultiSelect( fieldId, value ) {
1962 var setting = jQuery( '.frm_multiple_cont_' + fieldId );
1963 if ( value === 'select' ) {
1964 setting.fadeIn( 'fast' );
1965 } else {
1966 setting.fadeOut( 'fast' );
1967 }
1968 }
1969
1970 function toggleSepValues() {
1971 /*jshint validthis:true */
1972 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
1973 toggle( jQuery( '.field_' + fieldId + '_option_key' ) );
1974 jQuery( '.field_' + fieldId + '_option' ).toggleClass( 'frm_with_key' );
1975 }
1976
1977 function toggleMultiselect() {
1978 /*jshint validthis:true */
1979 var dropdown = jQuery( this ).closest( 'li' ).find( '.frm_form_fields select' );
1980 if ( this.checked ) {
1981 dropdown.attr( 'multiple', 'multiple' );
1982 } else {
1983 dropdown.removeAttr( 'multiple' );
1984 }
1985 }
1986
1987 /**
1988 * Allow typing on form switcher click without an extra click to search.
1989 */
1990 function focusSearchBox() {
1991 var searchBox = document.getElementById( 'dropform-search-input' );
1992 if ( searchBox !== null ) {
1993 setTimeout( function() {
1994 searchBox.focus();
1995 }, 100 );
1996 }
1997 }
1998
1999 /**
2000 * If a field is clicked in the builder, prevent inputs from changing.
2001 */
2002 function stopFieldFocus( e ) {
2003 e.preventDefault();
2004 }
2005
2006 function deleteFieldOption() {
2007 /*jshint validthis:true */
2008 var otherInput,
2009 parentLi = this.parentNode,
2010 parentUl = parentLi.parentNode,
2011 fieldId = this.getAttribute( 'data-fid' );
2012
2013 jQuery( parentLi ).fadeOut( 'slow', function() {
2014 jQuery( parentLi ).remove();
2015
2016 var hasOther = jQuery( parentUl ).find( '.frm_other_option' );
2017 if ( hasOther.length < 1 ) {
2018 otherInput = document.getElementById( 'other_input_' + fieldId );
2019 if ( otherInput !== null ) {
2020 otherInput.value = 0;
2021 }
2022 jQuery( '#other_button_' + fieldId ).fadeIn( 'slow' );
2023 }
2024 });
2025 }
2026
2027 /**
2028 * If a radio button is set as default, allow a click to
2029 * deselect it.
2030 */
2031 function maybeUncheckRadio() {
2032 /*jshint validthis:true */
2033 var $self = jQuery( this );
2034 if ( $self.is( ':checked' ) ) {
2035 var uncheck = function() {
2036 setTimeout( function() {
2037 $self.removeAttr( 'checked' );
2038 }, 0 );
2039 };
2040 var unbind = function() {
2041 $self.unbind( 'mouseup', up );
2042 };
2043 var up = function() {
2044 uncheck();
2045 unbind();
2046 };
2047 $self.bind( 'mouseup', up );
2048 $self.one( 'mouseout', unbind );
2049 }
2050 }
2051
2052 /**
2053 * If the field option has the default text, clear it out on click.
2054 */
2055 function maybeClearOptText() {
2056 /*jshint validthis:true */
2057 if ( this.value === frm_admin_js.new_option ) {
2058 this.value = '';
2059 }
2060 }
2061
2062 function clickDeleteField() {
2063 /*jshint validthis:true */
2064 var confirmMsg = frm_admin_js.conf_delete,
2065 maybeDivider = this.parentNode.parentNode.parentNode,
2066 li = maybeDivider.parentNode,
2067 field = jQuery( this ).closest( 'li' ),
2068 fieldId = field.data( 'fid' );
2069
2070 if ( li.classList.contains( 'frm-section-collapsed' ) || li.classList.contains( 'frm-page-collapsed' ) ) {
2071 return false;
2072 }
2073
2074 // If deleting a section, use a special message.
2075 if ( maybeDivider.className === 'divider_section_only' ) {
2076 confirmMsg = frm_admin_js.conf_delete_sec;
2077 this.setAttribute( 'data-frmcaution', frm_admin_js.caution );
2078 }
2079
2080 this.setAttribute( 'data-frmverify', confirmMsg );
2081 this.setAttribute( 'data-deletefield', fieldId );
2082
2083 confirmLinkClick( this );
2084 return false;
2085 }
2086
2087 function deleteFieldConfirmed() {
2088 /*jshint validthis:true */
2089 deleteFields( this.getAttribute( 'data-deletefield' ) );
2090 }
2091
2092 function deleteFields( fieldId ) {
2093 var field = jQuery( '#frm_field_id_' + fieldId );
2094
2095 deleteField( fieldId );
2096
2097 if ( field.hasClass( 'edit_field_type_divider' ) ) {
2098 field.find( 'li.frm_field_box' ).each( function() {
2099 //TODO: maybe delete only end section
2100 //if(n.hasClass('edit_field_type_end_divider')){
2101 deleteField( this.getAttribute( 'data-fid' ) );
2102 //}
2103 });
2104 }
2105 toggleSectionHolder();
2106 }
2107
2108 function deleteField( fieldId ) {
2109 jQuery.ajax({
2110 type: 'POST',
2111 url: ajaxurl,
2112 data: {
2113 action: 'frm_delete_field',
2114 field_id: fieldId,
2115 nonce: frmGlobal.nonce
2116 },
2117 success: function() {
2118 var $thisField = jQuery( document.getElementById( 'frm_field_id_' + fieldId ) ),
2119 settings = jQuery( '#frm-single-settings-' + fieldId );
2120
2121 // Remove settings from sidebar.
2122 if ( settings.is( ':visible' ) ) {
2123 document.getElementById( 'frm_insert_fields_tab' ).click();
2124 }
2125 settings.remove();
2126
2127 $thisField.fadeOut( 'slow', function() {
2128 var $section = $thisField.closest( '.start_divider' ),
2129 type = $thisField.data( 'type' );
2130 $thisField.remove();
2131 if ( type === 'break' ) {
2132 renumberPageBreaks();
2133 }
2134 if ( type === 'summary' ) {
2135 reenableAddSummaryBtn();
2136 }
2137 if ( type === 'product' ) {
2138 maybeHideQuantityProductFieldOption();
2139 // a product field attached to a quantity field earlier might be the one deleted, so re-populate
2140 popAllProductFields();
2141 }
2142 if ( jQuery( '#frm-show-fields li' ).length === 0 ) {
2143 document.getElementById( 'frm_form_editor_container' ).classList.remove( 'frm-has-fields' );
2144 } else if ( $section.length ) {
2145 toggleOneSectionHolder( $section );
2146 }
2147 });
2148 }
2149 });
2150 }
2151
2152 function addFieldLogicRow() {
2153 /*jshint validthis:true */
2154 var id = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' ),
2155 formId = thisFormId,
2156 metaName = 0;
2157
2158 if ( jQuery( '#frm_logic_row_' + id + ' .frm_logic_row' ).length > 0 ) {
2159 metaName = 1 + parseInt( jQuery( '#frm_logic_row_' + id + ' .frm_logic_row:last' ).attr( 'id' ).replace( 'frm_logic_' + id + '_', '' ), 10 );
2160 }
2161 jQuery.ajax({
2162 type: 'POST',
2163 url: ajaxurl,
2164 data: {
2165 action: 'frm_add_logic_row',
2166 form_id: formId,
2167 field_id: id,
2168 nonce: frmGlobal.nonce,
2169 meta_name: metaName,
2170 fields: getFieldList()
2171 },
2172 success: function( html ) {
2173 jQuery( document.getElementById( 'logic_' + id ) ).fadeOut( 'slow', function() {
2174 var logicRow = jQuery( document.getElementById( 'frm_logic_row_' + id ) );
2175 logicRow.append( html );
2176 logicRow.closest( '.frm_logic_rows' ).fadeIn( 'slow' );
2177 });
2178 }
2179 });
2180 return false;
2181 }
2182
2183 function addWatchLookupRow() {
2184 /*jshint validthis:true */
2185 var lastRowId,
2186 id = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' ),
2187 formId = thisFormId,
2188 rowKey = 0,
2189 lookupBlockRows = document.getElementById( 'frm_watch_lookup_block_' + id ).children;
2190
2191 if ( lookupBlockRows.length > 0 ) {
2192 lastRowId = lookupBlockRows[lookupBlockRows.length - 1].id;
2193 rowKey = 1 + parseInt( lastRowId.replace( 'frm_watch_lookup_' + id + '_', '' ), 10 );
2194 }
2195
2196 jQuery.ajax({
2197 type: 'POST',
2198 url: ajaxurl,
2199 data: {
2200 action: 'frm_add_watch_lookup_row',
2201 form_id: formId,
2202 field_id: id,
2203 row_key: rowKey,
2204 nonce: frmGlobal.nonce
2205 },
2206 success: function( newRow ) {
2207 var watchRowBlock = jQuery( document.getElementById( 'frm_watch_lookup_block_' + id ) );
2208 watchRowBlock.append( newRow );
2209 watchRowBlock.fadeIn( 'slow' );
2210 }
2211 });
2212 return false;
2213 }
2214
2215 function updateGetValueFieldSelection() {
2216 /*jshint validthis:true */
2217 var fieldID = this.id.replace( 'get_values_form_', '' );
2218 var fieldSelect = document.getElementById( 'get_values_field_' + fieldID );
2219 var fieldType = this.getAttribute( 'data-fieldtype' );
2220
2221 if ( this.value === '' ) {
2222 fieldSelect.options.length = 1;
2223 } else {
2224 var formID = this.value;
2225 jQuery.ajax({
2226 type: 'POST', url: ajaxurl,
2227 data: {
2228 action: 'frm_get_options_for_get_values_field',
2229 form_id: formID,
2230 field_type: fieldType,
2231 nonce: frmGlobal.nonce
2232 },
2233 success: function( fields ) {
2234 fieldSelect.innerHTML = fields;
2235 }
2236 });
2237 }
2238 }
2239
2240 // Clear the Watch Fields option when Lookup field switches to "Text" option
2241 function maybeClearWatchFields() {
2242 /*jshint validthis:true */
2243 var link, lookupBlock,
2244 fieldID = this.name.replace( 'field_options[data_type_', '' ).replace( ']', '' );
2245
2246 if ( this.value === 'text' ) {
2247 lookupBlock = document.getElementById( 'frm_watch_lookup_block_' + fieldID );
2248 if ( lookupBlock !== null ) {
2249 // Clear the Watch Fields option
2250 lookupBlock.innerHTML = '';
2251
2252 // Hide the Watch Fields row
2253 link = document.getElementById( 'frm_add_watch_lookup_link_' + fieldID ).parentNode;
2254 link.style.display = 'none';
2255 link.previousElementSibling.style.display = 'none';
2256 link.previousElementSibling.previousElementSibling.style.display = 'none';
2257 link.previousElementSibling.previousElementSibling.previousElementSibling.style.display = 'none';
2258 }
2259 }
2260
2261 toggleMultiSelect( fieldID, this.value );
2262 }
2263
2264 // Number the pages and hide/show the first page as needed.
2265 function renumberPageBreaks() {
2266 var i, containerClass,
2267 pages = document.getElementsByClassName( 'frm-page-num' );
2268
2269 if ( pages.length > 1 ) {
2270 document.getElementById( 'frm-fake-page' ).style.display = 'block';
2271 for ( i = 0; i < pages.length; i++ ) {
2272 containerClass = pages[i].parentNode.parentNode.parentNode.classList;
2273 if ( i === 1 ) {
2274 // Hide previous button on page 1
2275 containerClass.add( 'frm-first-page' );
2276 } else {
2277 containerClass.remove( 'frm-first-page' );
2278 }
2279 pages[i].innerHTML = ( i + 1 );
2280 }
2281 } else {
2282 document.getElementById( 'frm-fake-page' ).style.display = 'none';
2283 }
2284 }
2285
2286 // The fake field works differently than real fields.
2287 function maybeCollapsePage() {
2288 /*jshint validthis:true */
2289 var field = jQuery( this ).closest( '.frm_field_box[data-ftype=break]' );
2290 if ( field.length ) {
2291 toggleCollapsePage( field );
2292 } else {
2293 toggleCollapseFakePage();
2294 }
2295 }
2296
2297 // Find all fields in a page and hide/show them
2298 function toggleCollapsePage( field ) {
2299 var toCollapse = field.nextUntil( '.frm_field_box[data-ftype=break]' );
2300 togglePage( field, toCollapse );
2301 }
2302
2303 function toggleCollapseFakePage() {
2304 var topLevel = document.getElementById( 'frm-fake-page' ),
2305 firstField = document.getElementById( 'frm-show-fields' ).firstElementChild,
2306 toCollapse = jQuery( firstField ).nextUntil( '.frm_field_box[data-ftype=break]' ).andSelf();
2307
2308 if ( firstField.getAttribute( 'data-ftype' ) === 'break' ) {
2309 // Don't collapse if the first field is a page break.
2310 return;
2311 }
2312
2313 togglePage( jQuery( topLevel ), toCollapse );
2314 }
2315
2316 function togglePage( field, toCollapse ) {
2317 var i,
2318 fieldCount = toCollapse.length,
2319 slide = Math.min( fieldCount, 3 );
2320
2321 if ( field.hasClass( 'frm-page-collapsed' ) ) {
2322 field.removeClass( 'frm-page-collapsed' );
2323 toCollapse.removeClass( 'frm-is-collapsed' );
2324 for ( i = 0; i < slide; i++ ) {
2325 if ( i === slide - 1 ) {
2326 jQuery( toCollapse[ i ]).slideDown( 150, function() {
2327 toCollapse.show();
2328 });
2329 } else {
2330 jQuery( toCollapse[ i ]).slideDown( 150 );
2331 }
2332 }
2333 } else {
2334 field.addClass( 'frm-page-collapsed' );
2335 toCollapse.addClass( 'frm-is-collapsed' );
2336 for ( i = 0; i < slide; i++ ) {
2337 if ( i === slide - 1 ) {
2338 jQuery( toCollapse[ i ]).slideUp( 150, function() {
2339 toCollapse.css( 'cssText', 'display:none !important;' );
2340 });
2341 } else {
2342 jQuery( toCollapse[ i ]).slideUp( 150 );
2343 }
2344 }
2345 }
2346 }
2347
2348 function maybeCollapseSection() {
2349 /*jshint validthis:true */
2350 var parentCont = this.parentNode.parentNode.parentNode.parentNode;
2351
2352 parentCont.classList.toggle( 'frm-section-collapsed' );
2353 }
2354
2355 function maybeCollapseSettings() {
2356 /*jshint validthis:true */
2357 this.classList.toggle( 'frm-collapsed' );
2358 }
2359
2360 function clickLabel() {
2361 if ( ! this.id ) {
2362 return;
2363 }
2364
2365 /*jshint validthis:true */
2366 var setting = document.querySelectorAll( '[data-changeme="' + this.id + '"]' )[0],
2367 fieldId = this.id.replace( 'field_label_', '' ),
2368 fieldType = document.getElementById( 'field_options_type_' + fieldId ),
2369 fieldTypeName = fieldType.value;
2370
2371 if ( typeof setting !== 'undefined' ) {
2372 if ( fieldType.tagName === 'SELECT' ) {
2373 fieldTypeName = fieldType.options[ fieldType.selectedIndex ].text.toLowerCase();
2374 } else {
2375 fieldTypeName = fieldTypeName.replace( '_', ' ' );
2376 }
2377
2378 fieldTypeName = normalizeFieldName( fieldTypeName );
2379
2380 setTimeout( function() {
2381 if ( setting.value.toLowerCase() === fieldTypeName ) {
2382 setting.select();
2383 } else {
2384 setting.focus();
2385 }
2386 }, 50 );
2387 }
2388 }
2389
2390 function clickDescription() {
2391 /*jshint validthis:true */
2392 var setting = document.querySelectorAll( '[data-changeme="' + this.id + '"]' )[0];
2393 if ( typeof setting !== 'undefined' ) {
2394 setTimeout( function() {
2395 setting.focus();
2396 autoExpandSettings( setting );
2397 }, 50 );
2398 }
2399 }
2400
2401 function autoExpandSettings( setting ) {
2402 var inSection = setting.closest( '.frm-collapse-me' );
2403 if ( inSection !== null ) {
2404 inSection.previousElementSibling.classList.remove( 'frm-collapsed' );
2405 }
2406 }
2407
2408 function normalizeFieldName( fieldTypeName ) {
2409 if ( fieldTypeName === 'divider' ) {
2410 fieldTypeName = 'section';
2411 } else if ( fieldTypeName === 'range' ) {
2412 fieldTypeName = 'slider';
2413 } else if ( fieldTypeName === 'data' ) {
2414 fieldTypeName = 'dynamic';
2415 } else if ( fieldTypeName === 'form' ) {
2416 fieldTypeName = 'embed form';
2417 }
2418 return fieldTypeName;
2419 }
2420
2421 function clickVis( e ) {
2422 /*jshint validthis:true */
2423 var currentClass = e.target.classList;
2424 if ( currentClass.contains( 'frm-collapse-page' ) || currentClass.contains( 'frm-sub-label' ) ) {
2425 return;
2426 }
2427
2428 if ( this.closest( '.start_divider' ) !== null ) {
2429 e.stopPropagation();
2430 }
2431 clickAction( this );
2432 }
2433
2434 /**
2435 * Open Advanced settings on double click.
2436 */
2437 function openAdvanced() {
2438 var fieldId = this.getAttribute( 'data-fid' );
2439 autoExpandSettings( document.getElementById( 'field_options_field_key_' + fieldId ) );
2440 }
2441
2442 function toggleRepeatButtons() {
2443 /*jshint validthis:true */
2444 var $thisField = jQuery( this ).closest( '.frm_field_box' );
2445 $thisField.find( '.repeat_icon_links' ).removeClass( 'repeat_format repeat_formatboth repeat_formattext' ).addClass( 'repeat_format' + this.value );
2446 if ( this.value === 'text' || this.value === 'both' ) {
2447 $thisField.find( '.frm_repeat_text' ).show();
2448 $thisField.find( '.repeat_icon_links a' ).addClass( 'frm_button' );
2449 } else {
2450 $thisField.find( '.frm_repeat_text' ).hide();
2451 $thisField.find( '.repeat_icon_links a' ).removeClass( 'frm_button' );
2452 }
2453 }
2454
2455 function checkRepeatLimit() {
2456 /*jshint validthis:true */
2457 var val = this.value;
2458 if ( val !== '' && ( val < 2 || val > 200 ) ) {
2459 infoModal( frm_admin_js.repeat_limit_min );
2460 this.value = '';
2461 }
2462 }
2463
2464 function checkCheckboxSelectionsLimit() {
2465 /*jshint validthis:true */
2466 var val = this.value;
2467 if ( val !== '' && ( val < 1 || val > 200 ) ) {
2468 infoModal( frm_admin_js.checkbox_limit );
2469 this.value = '';
2470 }
2471 }
2472
2473 function updateRepeatText( obj, addRemove ) {
2474 var $thisField = jQuery( obj ).closest( '.frm_field_box' );
2475 $thisField.find( '.frm_' + addRemove + '_form_row .frm_repeat_label' ).text( obj.value );
2476 }
2477
2478 function fieldsInSection( id ) {
2479 var children = [];
2480 jQuery( document.getElementById( 'frm_field_id_' + id ) ).find( 'li.frm_field_box:not(.no_repeat_section .edit_field_type_end_divider)' ).each( function() {
2481 children.push( jQuery( this ).data( 'fid' ) );
2482 });
2483 return children;
2484 }
2485
2486 function toggleFormTax() {
2487 /*jshint validthis:true */
2488 var id = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
2489 var val = this.value;
2490 var $showFields = document.getElementById( 'frm_show_selected_fields_' + id );
2491 var $showForms = document.getElementById( 'frm_show_selected_forms_' + id );
2492
2493 jQuery( $showForms ).find( 'select' ).val( '' );
2494 if ( val === 'form' ) {
2495 $showForms.style.display = 'inline';
2496 empty( $showFields );
2497 } else {
2498 $showFields.style.display = 'none';
2499 $showForms.style.display = 'none';
2500 getTaxOrFieldSelection( val, id );
2501 }
2502
2503 }
2504
2505 function resetOptOnChange() {
2506 /*jshint validthis:true */
2507 var field = getFieldKeyFromOpt( this ),
2508 thisOpt = jQuery( this ).closest( '.frm_single_option' );
2509
2510 resetSingleOpt( field.fieldId, field.fieldKey, thisOpt );
2511 }
2512
2513 function getFieldKeyFromOpt( object ) {
2514 var allOpts = jQuery( object ).closest( '.frm_sortable_field_opts' ),
2515 fieldId = allOpts.attr( 'id' ).replace( 'frm_field_', '' ).replace( '_opts', '' ),
2516 fieldKey = allOpts.data( 'key' );
2517
2518 return {
2519 fieldId: fieldId,
2520 fieldKey: fieldKey
2521 };
2522 }
2523
2524 function resetSingleOpt( fieldId, fieldKey, thisOpt ) {
2525 var saved, text, defaultVal, previewInput,
2526 optKey = thisOpt.data( 'optkey' ),
2527 separateValues = usingSeparateValues( fieldId ),
2528 single = jQuery( 'label[for="field_' + fieldKey + '-' + optKey + '"]' ),
2529 baseName = 'field_options[options_' + fieldId + '][' + optKey + ']';
2530 label = jQuery( 'input[name="' + baseName + '[label]"]' );
2531
2532 if ( single.length < 1 ) {
2533 resetDisplayedOpts( fieldId );
2534
2535 // Set the default value.
2536 defaultVal = thisOpt.find( 'input[name^="default_value_"]' );
2537 if ( defaultVal.is( ':checked' ) && label.length > 0 ) {
2538 jQuery( 'select[name^="item_meta[' + fieldId + ']"]' ).val( label.val() );
2539 }
2540 return;
2541 }
2542
2543 previewInput = single.children( 'input' );
2544
2545 if ( label.length < 1 ) {
2546 // Check for other label.
2547 label = jQuery( 'input[name="' + baseName + '"]' );
2548 saved = label.val();
2549 } else if ( separateValues ) {
2550 saved = jQuery( 'input[name="' + baseName + '[value]"]' ).val();
2551 } else {
2552 saved = label.val();
2553 }
2554
2555 if ( label.length ) {
2556 // Set the displayed value.
2557 text = single[0].childNodes;
2558 text[ text.length - 1 ].nodeValue = ' ' + label.val();
2559
2560 // Set saved value.
2561 previewInput.val( saved );
2562
2563 // Set the default value.
2564 defaultVal = thisOpt.find( 'input[name^="default_value_"]' );
2565 previewInput.prop( 'checked', defaultVal.is( ':checked' ) ? true : false );
2566 }
2567 }
2568
2569 function resetDisplayedOpts( fieldId ) {
2570 var i, opts, type, placeholder, fieldInfo,
2571 input = jQuery( '[name^="item_meta[' + fieldId + ']"]' );
2572
2573 if ( input.length < 1 ) {
2574 return;
2575 }
2576
2577 if ( input.is( 'select' ) ) {
2578 placeholder = document.getElementById( 'frm_placeholder_' + fieldId );
2579 if ( placeholder !== null && placeholder.value === '' ) {
2580 fillDropdownOpts( input[0], { sourceID: fieldId });
2581 } else {
2582 fillDropdownOpts( input[0], {
2583 sourceID: fieldId,
2584 placeholder: placeholder.value
2585 });
2586 }
2587 } else {
2588 opts = getMultipleOpts( fieldId );
2589 type = input.attr( 'type' );
2590 jQuery( '#field_' + fieldId + '_inner_container > .frm_form_fields' ).html( '' );
2591 fieldInfo = getFieldKeyFromOpt( jQuery( '#frm_delete_field_' + fieldId + '-000_container' ) );
2592 var container = jQuery( '#field_' + fieldId + '_inner_container > .frm_form_fields' ),
2593 isProduct = isProductField( fieldId );
2594
2595 for ( i = 0; i < opts.length; i++ ) {
2596 container.append( addRadioCheckboxOpt( type, opts[ i ], fieldId, fieldInfo.fieldKey, isProduct ) );
2597 }
2598 }
2599 }
2600
2601 function addRadioCheckboxOpt( type, opt, fieldId, fieldKey, isProduct ) {
2602 var other, single,
2603 isOther = opt.key.indexOf( 'other' ) !== -1,
2604 id = 'field_' + fieldKey + '-' + opt.key;
2605
2606 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="" />';
2607
2608 single = '<div class="frm_' + type + ' ' + type + '" id="frm_' + type + '_' + fieldId + '-' + opt.key + '"><label for="' + id +
2609 '"><input type="' + type +
2610 '" name="item_meta[' + fieldId + ']' + ( type === 'checkbox' ? '[]' : '' ) +
2611 '" value="' + opt.saved + '" id="' + id + '"' + ( isProduct ? ' data-price="' + opt.price + '"' : '' ) + '> ' + opt.label + '</label>' +
2612 ( isOther ? other : '' ) +
2613 '</div>';
2614
2615 return single;
2616 }
2617
2618 function fillDropdownOpts( field, atts ) {
2619 if ( field === null ) {
2620 return;
2621 }
2622 var sourceID = atts.sourceID,
2623 placeholder = atts.placeholder,
2624 isProduct = isProductField( sourceID ),
2625 showOther = atts.other;
2626
2627 removeDropdownOpts( field );
2628 var opts = getMultipleOpts( sourceID ),
2629 hasPlaceholder = ( typeof placeholder !== 'undefined' );
2630
2631 for ( var i = 0; i < opts.length; i++ ) {
2632 var label = opts[ i ].label,
2633 isOther = opts[ i ].key.indexOf( 'other' ) !== -1;
2634
2635 if ( hasPlaceholder && label !== '' ) {
2636 addBlankSelectOption( field, placeholder );
2637 } else if ( hasPlaceholder ) {
2638 label = placeholder;
2639 }
2640 hasPlaceholder = false;
2641
2642 if ( ! isOther || showOther ) {
2643 var opt = document.createElement( 'option' );
2644 opt.value = opts[ i ].saved;
2645 opt.innerHTML = label;
2646
2647 if ( isProduct ) {
2648 opt.setAttribute( 'data-price', opts[ i ].price );
2649 }
2650
2651 field.appendChild( opt );
2652 }
2653 }
2654 }
2655
2656 function addBlankSelectOption( field, placeholder ) {
2657 var opt = document.createElement( 'option' ),
2658 firstChild = field.firstChild;
2659
2660 opt.value = '';
2661 opt.innerHTML = placeholder;
2662 if ( firstChild !== null ) {
2663 field.insertBefore( opt, firstChild );
2664 field.selectedIndex = 0;
2665 } else {
2666 field.appendChild( opt );
2667 }
2668 }
2669
2670 function getMultipleOpts( fieldId ) {
2671 var i, saved, labelName, label, key, optObj,
2672 opts = [],
2673 optVals = jQuery( 'input[name^="field_options[options_' + fieldId + ']"]' ),
2674 separateValues = usingSeparateValues( fieldId ),
2675 isProduct = isProductField( fieldId );
2676
2677 for ( i = 0; i < optVals.length; i++ ) {
2678 if ( optVals[ i ].name.indexOf( '[000]' ) > 0 || optVals[ i ].name.indexOf( '[value]' ) > 0 || optVals[ i ].name.indexOf( '[price]' ) > 0 ) {
2679 continue;
2680 }
2681 saved = optVals[ i ].value;
2682 label = saved;
2683 key = optVals[ i ].name.replace( 'field_options[options_' + fieldId + '][', '' ).replace( '[label]', '' ).replace( ']', '' );
2684
2685 if ( separateValues ) {
2686 labelName = optVals[ i ].name.replace( '[label]', '[value]' );
2687 saved = jQuery( 'input[name="' + labelName + '"]' ).val();
2688 }
2689
2690 optObj = {
2691 saved: saved,
2692 label: label,
2693 key: key
2694 };
2695
2696 if ( isProduct ) {
2697 labelName = optVals[ i ].name.replace( '[label]', '[price]' );
2698 optObj.price = jQuery( 'input[name="' + labelName + '"]' ).val();
2699 }
2700
2701 opts.push( optObj );
2702 }
2703
2704 return opts;
2705 }
2706
2707 function removeDropdownOpts( field ) {
2708 var i;
2709 if ( typeof field.options === 'undefined' ) {
2710 return;
2711 }
2712
2713 for ( i = field.options.length - 1; i >= 0; i-- ) {
2714 field.remove( i );
2715 }
2716 }
2717
2718 /**
2719 * Is the box checked to use separate values?
2720 */
2721 function usingSeparateValues( fieldId ) {
2722 var field = document.getElementById( 'separate_value_' + fieldId );
2723 if ( field === null ) {
2724 return false;
2725 } else {
2726 return field.checked;
2727 }
2728 }
2729
2730 /* TODO: Is this still used? */
2731 function checkUniqueOpt( id, text ) {
2732 if ( id.indexOf( 'field_key_' ) === 0 ) {
2733 var a = id.split( '-' );
2734 jQuery.each( jQuery( 'label[id^="' + a[0] + '"]' ), function( k, v ) {
2735 var c = false;
2736 if ( ! c && jQuery( v ).attr( 'id' ) != id && jQuery( v ).html() == text ) {
2737 c = true;
2738 infoModal( 'Saved values cannot be identical.' );
2739 }
2740 });
2741 }
2742 }
2743
2744 function setStarValues() {
2745 /*jshint validthis:true */
2746 var fieldID = this.id.replace( 'radio_maxnum_', '' );
2747 var container = jQuery( '#field_' + fieldID + '_inner_container .frm-star-group' );
2748 var fieldKey = document.getElementsByName( 'field_options[field_key_' + fieldID + ']' )[0].value;
2749 container.html( '' );
2750
2751 var min = 1;
2752 var max = this.value;
2753 if ( min > max ) {
2754 max = min;
2755 }
2756
2757 for ( var i = min; i <= max; i++ ) {
2758 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>' );
2759 }
2760 }
2761
2762 function setScaleValues() {
2763 /*jshint validthis:true */
2764 var isMin = this.id.indexOf( 'minnum' ) !== -1;
2765 var fieldID = this.id.replace( 'scale_maxnum_', '' ).replace( 'scale_minnum_', '' );
2766 var min = this.value;
2767 var max = this.value;
2768 if ( isMin ) {
2769 max = document.getElementById( 'scale_maxnum_' + fieldID ).value;
2770 } else {
2771 min = document.getElementById( 'scale_minnum_' + fieldID ).value;
2772 }
2773
2774 updateScaleValues( parseInt( min, 10 ), parseInt( max, 10 ), fieldID );
2775 }
2776
2777 function updateScaleValues( min, max, fieldID ) {
2778 var container = jQuery( '#field_' + fieldID + '_inner_container .frm_form_fields' );
2779 container.html( '' );
2780
2781 if ( min >= max ) {
2782 max = min + 1;
2783 }
2784
2785 for ( var i = min; i <= max; i++ ) {
2786 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>' );
2787 }
2788 container.append( '<div class="clear"></div>' );
2789 }
2790
2791 function getFieldValues() {
2792 /*jshint validthis:true */
2793 var isTaxonomy,
2794 val = this.value;
2795
2796 if ( val ) {
2797 var parentIDs = this.parentNode.id.replace( 'frm_logic_', '' ).split( '_' );
2798 var fieldID = parentIDs[0];
2799 var metaKey = parentIDs[1];
2800 var valueField = document.getElementById( 'frm_field_id_' + val );
2801 var valueFieldType = valueField.getAttribute( 'data-ftype' );
2802 var fill = document.getElementById( 'frm_show_selected_values_' + fieldID + '_' + metaKey );
2803 var optionName = 'field_options[hide_opt_' + fieldID + '][]';
2804 var optionID = 'frm_field_logic_opt_' + fieldID;
2805 var input = false;
2806 var showSelect = ( valueFieldType === 'select' || valueFieldType === 'checkbox' || valueFieldType === 'radio' );
2807 var showText = ( valueFieldType === 'text' || valueFieldType === 'email' || valueFieldType === 'phone' || valueFieldType === 'url' || valueFieldType === 'number' );
2808
2809 if ( showSelect ) {
2810 isTaxonomy = document.getElementById( 'frm_has_hidden_options_' + val );
2811 if ( isTaxonomy !== null ) {
2812 // get the category options with ajax
2813 showSelect = false;
2814 }
2815 }
2816
2817 if ( showSelect || showText ) {
2818 fill.innerHTML = '';
2819 if ( showSelect ) {
2820 input = document.createElement( 'select' );
2821 } else {
2822 input = document.createElement( 'input' );
2823 input.type = 'text';
2824 }
2825 input.name = optionName;
2826 input.id = optionID + '_' + metaKey;
2827 fill.appendChild( input );
2828
2829 if ( showSelect ) {
2830 var fillField = document.getElementById( input.id );
2831 fillDropdownOpts( fillField, {
2832 sourceID: val,
2833 placeholder: '',
2834 other: true
2835 });
2836 }
2837 } else {
2838 var thisType = this.getAttribute( 'data-type' );
2839 frmGetFieldValues( val, fieldID, metaKey, thisType );
2840 }
2841 }
2842 }
2843
2844 function getFieldSelection() {
2845 /*jshint validthis:true */
2846 var formId = this.value;
2847 if ( formId ) {
2848 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
2849 getTaxOrFieldSelection( formId, fieldId );
2850 }
2851 }
2852
2853 function getTaxOrFieldSelection( formId, fieldId ) {
2854 if ( formId ) {
2855 jQuery.ajax({
2856 type: 'POST',
2857 url: ajaxurl,
2858 data: {
2859 action: 'frm_get_field_selection',
2860 field_id: fieldId,
2861 form_id: formId,
2862 nonce: frmGlobal.nonce
2863 },
2864 success: function( msg ) {
2865 jQuery( '#frm_show_selected_fields_' + fieldId ).html( msg ).show();
2866 }
2867 });
2868 }
2869 }
2870
2871 function updateFieldOrder() {
2872
2873 renumberPageBreaks();
2874 jQuery( '#frm-show-fields' ).each( function( i ) {
2875 var fields = jQuery( 'li.frm_field_box', this );
2876 for ( i = 0; i < fields.length; i++ ) {
2877 var fieldId = fields[ i ].getAttribute( 'data-fid' ),
2878 field = jQuery( 'input[name="field_options[field_order_' + fieldId + ']"]' ),
2879 currentOrder = field.val(),
2880 newOrder = ( i + 1 );
2881
2882 if ( currentOrder != newOrder ) {
2883 field.val( newOrder );
2884 singleField = document.getElementById( 'frm-single-settings-' + fieldId );
2885
2886 moveFieldSettings( singleField );
2887 }
2888 }
2889 });
2890 }
2891
2892 function toggleSectionHolder() {
2893 jQuery( '.start_divider' ).each( function() {
2894 toggleOneSectionHolder( jQuery( this ) );
2895 });
2896 }
2897
2898 function toggleOneSectionHolder( $section ) {
2899 if ( $section.length === 0 ) {
2900 return;
2901 }
2902
2903 var sectionFields = $section.parent( '.frm_field_box' ).children( '.frm_no_section_fields' );
2904 if ( $section.children( 'li' ).length < 2 ) {
2905 sectionFields.addClass( 'frm_block' );
2906 } else {
2907 sectionFields.removeClass( 'frm_block' );
2908 }
2909 }
2910
2911 function slideDown() {
2912 /*jshint validthis:true */
2913 var id = jQuery( this ).data( 'slidedown' );
2914 var $thisId = jQuery( document.getElementById( id ) );
2915 if ( $thisId.is( ':hidden' ) ) {
2916 $thisId.slideDown( 'fast' );
2917 this.style.display = 'none';
2918 }
2919 return false;
2920 }
2921
2922 function slideUp() {
2923 /*jshint validthis:true */
2924 var id = jQuery( this ).data( 'slideup' );
2925 var $thisId = jQuery( document.getElementById( id ) );
2926 $thisId.slideUp( 'fast' );
2927 $thisId.siblings( 'a' ).show();
2928 return false;
2929 }
2930
2931 /**
2932 * Get rid of empty container that inserts extra space.
2933 */
2934 function hideEmptyEle() {
2935 jQuery( '.frm-hide-empty' ).each( function() {
2936 if ( jQuery( this ).text().trim().length === 0 ) {
2937 jQuery( this ).remove();
2938 }
2939 });
2940 }
2941
2942 /* Change the classes in the builder */
2943 function changeFieldClass( field, setting ) {
2944 var classes, replace, alignField,
2945 replaceWith = ' ' + setting.value,
2946 fieldId = field.getAttribute( 'data-fid' );
2947
2948 // Include classes from multiple settings.
2949 if ( typeof fieldId !== 'undefined' ) {
2950 if ( setting.classList.contains( 'field_options_align' ) ) {
2951 replaceWith += ' ' + document.getElementById( 'frm_classes_' + fieldId ).value;
2952 } else if ( setting.classList.contains( 'frm_classes' ) ) {
2953 alignField = document.getElementById( 'field_options_align_' + fieldId );
2954 if ( alignField !== null ) {
2955 replaceWith += ' ' + alignField.value;
2956 }
2957 }
2958 }
2959 replaceWith += ' ';
2960
2961 // Allow for the column number dropdown.
2962 replaceWith = replaceWith.replace( ' block ', ' ' ).replace( ' inline ', ' horizontal_radio ' ).replace( ' frm_alignright ', ' ' );
2963
2964 classes = field.className.split( ' frmstart ' )[1].split( ' frmend ' )[0];
2965 if ( classes.trim() === '' ) {
2966 replace = ' frmstart frmend ';
2967 replaceWith = ' frmstart ' + replaceWith.trim() + ' frmend ';
2968 } else {
2969 replace = classes.trim();
2970 replaceWith = replaceWith.trim();
2971 }
2972 field.className = field.className.replace( replace, replaceWith );
2973 }
2974
2975 function maybeShowInlineModal( e ) {
2976 /*jshint validthis:true */
2977 e.preventDefault();
2978 showInlineModal( this );
2979 }
2980
2981 function showInlineModal( icon, input ) {
2982 var box = document.getElementById( icon.getAttribute( 'data-open' ) ),
2983 container = jQuery( icon ).closest( 'p' ),
2984 inputTrigger = ( typeof input !== 'undefined' );
2985
2986 if ( container.hasClass( 'frm-open' ) ) {
2987 container.removeClass( 'frm-open' );
2988 box.classList.add( 'frm_hidden' );
2989 } else {
2990 if ( ! inputTrigger ) {
2991 input = getInputForIcon( icon );
2992 }
2993 if ( input !== null ) {
2994 if ( ! inputTrigger ) {
2995 input.focus();
2996 }
2997 container.after( box );
2998 box.setAttribute( 'data-fills', input.id );
2999
3000 if ( box.id.indexOf( 'frm-calc-box' ) === 0 ) {
3001 popCalcFields( box, true );
3002 }
3003 }
3004
3005 container.addClass( 'frm-open' );
3006 box.classList.remove( 'frm_hidden' );
3007 }
3008 }
3009
3010 function dismissInlineModal( e ) {
3011 /*jshint validthis:true */
3012 e.preventDefault();
3013 this.parentNode.classList.add( 'frm_hidden' );
3014 jQuery( '.frm-open [data-open="' + this.parentNode.id + '"]' ).closest( '.frm-open' ).removeClass( 'frm-open' );
3015 }
3016
3017 function changeInputtedValue() {
3018 /*jshint validthis:true */
3019 var i,
3020 action = this.getAttribute( 'data-frmchange' ).split( ',' );
3021
3022 for ( i = 0; i < action.length; i++ ) {
3023 if ( action[i] === 'updateOption' ) {
3024 changeHiddenSeparateValue( this );
3025 } else if ( action[i] === 'updateDefault' ) {
3026 changeDefaultRadioValue( this );
3027 } else {
3028 this.value = this.value[ action[i] ]();
3029 }
3030 }
3031 }
3032
3033 /**
3034 * When the saved value is changed, update the default value radio.
3035 */
3036 function changeDefaultRadioValue( input ) {
3037 var parentLi = getOptionParent( input ),
3038 key = parentLi.getAttribute( 'data-optkey' ),
3039 fieldId = getOptionFieldId( parentLi, key ),
3040 defaultRadio = parentLi.querySelector( 'input[name="default_value_' + fieldId + '"]' );
3041
3042 defaultRadio.value = input.value;
3043 }
3044
3045 /**
3046 * If separate values are not enabled, change the saved value when
3047 * the displayed value is changed.
3048 */
3049 function changeHiddenSeparateValue( input ) {
3050 var savedVal,
3051 parentLi = getOptionParent( input ),
3052 key = parentLi.getAttribute( 'data-optkey' ),
3053 fieldId = getOptionFieldId( parentLi, key ),
3054 sep = document.getElementById( 'separate_value_' + fieldId );
3055
3056 if ( sep.checked === false ) {
3057 // If separate values are not turned on.
3058 savedVal = document.getElementById( 'field_key_' + fieldId + '-' + key );
3059 savedVal.value = input.value;
3060 changeDefaultRadioValue( savedVal );
3061 }
3062 }
3063
3064 function getOptionParent( input ) {
3065 var parentLi = input.parentNode;
3066 if ( parentLi.tagName !== 'LI' ) {
3067 parentLi = parentLi.parentNode;
3068 }
3069 return parentLi;
3070 }
3071
3072 function getOptionFieldId( li, key ) {
3073 var liId = li.id;
3074
3075 return liId.replace( 'frm_delete_field_', '' ).replace( '-' + key + '_container', '' );
3076 }
3077
3078 function submitBuild() {
3079 /*jshint validthis:true */
3080 var $thisEle = jQuery( this );
3081 var p = $thisEle.html();
3082
3083 preFormSave( this );
3084
3085 var $form = jQuery( builderForm );
3086 var v = JSON.stringify( $form.serializeArray() );
3087
3088 jQuery( document.getElementById( 'frm_compact_fields' ) ).val( v );
3089 jQuery.ajax({
3090 type: 'POST',
3091 url: ajaxurl,
3092 data: {action: 'frm_save_form', 'frm_compact_fields': v, nonce: frmGlobal.nonce},
3093 success: function( msg ) {
3094 afterFormSave( $thisEle, p );
3095
3096 var $postStuff = document.getElementById( 'post-body-content' );
3097 var $html = document.createElement( 'div' );
3098 $html.setAttribute( 'class', 'frm_updated_message' );
3099 $html.innerHTML = msg;
3100 $postStuff.insertBefore( $html, $postStuff.firstChild );
3101 },
3102 error: function() {
3103 jQuery( document.getElementById( 'frm_js_build_form' ) ).submit();
3104 }
3105 });
3106 }
3107
3108 function submitNoAjax() {
3109 /*jshint validthis:true */
3110 preFormSave( this );
3111
3112 var form = jQuery( builderForm );
3113 jQuery( document.getElementById( 'frm_compact_fields' ) ).val( JSON.stringify( form.serializeArray() ) );
3114 jQuery( document.getElementById( 'frm_js_build_form' ) ).submit();
3115 }
3116
3117 function preFormSave( b ) {
3118 removeWPUnload();
3119 if ( jQuery( 'form.inplace_form' ).length ) {
3120 jQuery( '.inplace_save, .postbox' ).click();
3121 }
3122
3123 $button = jQuery( b );
3124
3125 if ( $button.hasClass( 'frm_button_submit' ) ) {
3126 $button.addClass( 'frm_loading_form' );
3127 $button.html( frm_admin_js.saving );
3128 } else {
3129 $button.addClass( 'frm_loading_button' );
3130 $button.val( frm_admin_js.saving );
3131 }
3132 }
3133
3134 function afterFormSave( $button, buttonVal ) {
3135 $button.removeClass( 'frm_loading_form' ).removeClass( 'frm_loading_button' );
3136 $button.html( frm_admin_js.saved );
3137
3138 setTimeout( function() {
3139 jQuery( '.frm_updated_message' ).fadeOut( 'slow', function() {
3140 this.parentNode.removeChild( this );
3141 });
3142 $button.fadeOut( 'slow', function() {
3143 $button.html( buttonVal );
3144 $button.show();
3145 });
3146 }, 5000 );
3147 }
3148
3149 function initUpgradeModal() {
3150 var $info = initModal( '#frm_upgrade_modal' );
3151 if ( $info === false ) {
3152 return;
3153 }
3154
3155 jQuery( document ).on( 'click', '[data-upgrade]', function( event ) {
3156 event.preventDefault();
3157 jQuery( '#frm_upgrade_modal .frm_lock_icon' ).removeClass( 'frm_lock_open_icon' );
3158 jQuery( '#frm_upgrade_modal .frm_lock_icon use' ).attr( 'xlink:href', '#frm_lock_icon' );
3159
3160 var requires = this.getAttribute( 'data-requires' );
3161 if ( typeof requires === 'undefined' || requires === null || requires === '' ) {
3162 requires = 'Pro';
3163 }
3164 jQuery( '.license-level' ).html( requires );
3165
3166 // If one click upgrade, hide other content
3167 addOneClickModal( this );
3168
3169 jQuery( '.frm_feature_label' ).html( this.getAttribute( 'data-upgrade' ) );
3170 jQuery( '#frm_upgrade_modal h2' ).show();
3171
3172 $info.dialog( 'open' );
3173
3174 // set the utm medium
3175 var button = $info.find( '.button-primary:not(#frm-oneclick-button)' );
3176 var link = button.attr( 'href' ).replace( /(medium=)[a-z_-]+/ig, '$1' + this.getAttribute( 'data-medium' ) );
3177 var content = this.getAttribute( 'data-content' );
3178 if ( content === undefined ) {
3179 content = '';
3180 }
3181 link = link.replace( /(content=)[a-z_-]+/ig, '$1' + content );
3182 button.attr( 'href', link );
3183 return false;
3184 });
3185 }
3186
3187 /**
3188 * Allow addons to be installed from the upgrade modal.
3189 */
3190 function addOneClickModal( link ) {
3191 var oneclickMessage = document.getElementById( 'frm-oneclick' ),
3192 oneclick = link.getAttribute( 'data-oneclick' ),
3193 customLink = link.getAttribute( 'data-link' ),
3194 showLink = document.getElementById( 'frm-upgrade-modal-link' ),
3195 upgradeMessage = document.getElementById( 'frm-upgrade-message' ),
3196 newMessage = link.getAttribute( 'data-message' ),
3197 button = document.getElementById( 'frm-oneclick-button' ),
3198 showIt = 'block',
3199 hideIt = 'none';
3200
3201 // If one click upgrade, hide other content.
3202 if ( oneclickMessage !== null && typeof oneclick !== 'undefined' && oneclick ) {
3203 showIt = 'none';
3204 hideIt = 'block';
3205 oneclick = JSON.parse( oneclick );
3206
3207 button.className = button.className.replace( ' frm-install-addon', '' ).replace( ' frm-activate-addon', '' );
3208 button.className = button.className + ' ' + oneclick.class;
3209 button.rel = oneclick.url;
3210 }
3211
3212 // Use a custom message in the modal.
3213 if ( newMessage === null || typeof newMessage === 'undefined' || newMessage === '' ) {
3214 newMessage = upgradeMessage.getAttribute( 'data-default' );
3215 }
3216 upgradeMessage.innerHTML = newMessage;
3217
3218 // Either set the link or use the default.
3219 if ( customLink === null || typeof customLink === 'undefined' || customLink === '' ) {
3220 customLink = showLink.getAttribute( 'data-default' );
3221 }
3222 showLink.href = customLink;
3223
3224 document.getElementById( 'frm-addon-status' ).style.display = 'none';
3225 oneclickMessage.style.display = hideIt;
3226 button.style.display = hideIt === 'block' ? 'inline-block' : hideIt;
3227 upgradeMessage.style.display = showIt;
3228 showLink.style.display = showIt === 'block' ? 'inline-block' : showIt;
3229 }
3230
3231 /* Form settings */
3232
3233 function showInputIcon( parentClass ) {
3234 if ( typeof parentClass === 'undefined' ) {
3235 parentClass = '';
3236 }
3237 maybeAddFieldSelection( parentClass );
3238 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>' );
3239 }
3240
3241 /**
3242 * For reverse compatibility. Check for fields that were
3243 * using the old sidebar.
3244 */
3245 function maybeAddFieldSelection( parentClass ) {
3246 var i,
3247 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' );
3248 for ( i = 0; i < missingClass.length; i++ ) {
3249 missingClass[i].parentNode.classList.add( 'frm_has_shortcodes' );
3250 }
3251 }
3252
3253 function showSuccessOpt() {
3254 /*jshint validthis:true */
3255 var c = 'success';
3256 if ( this.name === 'options[edit_action]' ) {
3257 c = 'edit';
3258 }
3259 var v = jQuery( this ).val();
3260 jQuery( '.' + c + '_action_box' ).hide();
3261 if ( v === 'redirect' ) {
3262 jQuery( '.' + c + '_action_redirect_box.' + c + '_action_box' ).fadeIn( 'slow' );
3263 } else if ( v === 'page' ) {
3264 jQuery( '.' + c + '_action_page_box.' + c + '_action_box' ).fadeIn( 'slow' );
3265 } else {
3266 jQuery( '.' + c + '_action_message_box.' + c + '_action_box' ).fadeIn( 'slow' );
3267 }
3268 }
3269
3270 function copyFormAction() {
3271 /*jshint validthis:true */
3272 var action = jQuery( this ).closest( '.frm_form_action_settings' ).clone();
3273 var currentID = action.attr( 'id' ).replace( 'frm_form_action_', '' );
3274 var newID = newActionId( currentID );
3275 action.find( '.frm_action_id, .frm-btn-group' ).remove();
3276 action.find( 'input[name$="[' + currentID + '][ID]"]' ).val( '' );
3277 action.find( '.widget-inside' ).hide();
3278
3279 // the .html() gets original values, so they need to be set
3280 action.find( 'input[type=text], textarea, input[type=number]' ).prop( 'defaultValue', function() {
3281 return this.value;
3282 });
3283
3284 action.find( 'input[type=checkbox], input[type=radio]' ).prop( 'defaultChecked', function() {
3285 return this.checked;
3286 });
3287
3288 var rename = new RegExp( '\\[' + currentID + '\\]', 'g' );
3289 var reid = new RegExp( '_' + currentID + '"', 'g' );
3290 var reclass = new RegExp( '-' + currentID + '"', 'g' );
3291 var revalue = new RegExp( '"' + currentID + '"', 'g' ); // if a field id matches, this could cause trouble
3292
3293 var html = action.html().replace( rename, '[' + newID + ']' ).replace( reid, '_' + newID + '"' );
3294 html = html.replace( reclass, '-' + newID + '"' ).replace( revalue, '"' + newID + '"' );
3295 var div = '<div id="frm_form_action_' + newID + '" class="widget frm_form_action_settings frm_single_email_settings" data-actionkey="' + newID + '">';
3296
3297 jQuery( '#frm_notification_settings' ).append( div + html + '</div>' );
3298 initiateMultiselect();
3299 }
3300
3301 function newActionId( currentID ) {
3302 var newID = parseInt( currentID, 10 ) + 11;
3303 var exists = document.getElementById( 'frm_form_action_' + newID );
3304 if ( exists !== null ) {
3305 newID++;
3306 newID = newActionId( newID );
3307 }
3308 return newID;
3309 }
3310
3311 function addFormAction() {
3312 /*jshint validthis:true */
3313 var actionId = getNewActionId();
3314 var type = jQuery( this ).data( 'actiontype' );
3315 var formId = thisFormId;
3316
3317 jQuery.ajax({
3318 type: 'POST', url: ajaxurl,
3319 data: {
3320 action: 'frm_add_form_action',
3321 type: type,
3322 list_id: actionId,
3323 form_id: formId,
3324 nonce: frmGlobal.nonce
3325 },
3326 success: function( html ) {
3327 // Close any open actions first.
3328 jQuery( '.frm_form_action_settings.open' ).removeClass( 'open' );
3329
3330 jQuery( '#frm_notification_settings' ).append( html );
3331 jQuery( '.frm_form_action_settings' ).fadeIn( 'slow' );
3332
3333 var newAction = document.getElementById( 'frm_form_action_' + actionId );
3334
3335 newAction.classList.add( 'open' );
3336 document.getElementById( 'post-body-content' ).scroll({
3337 top: newAction.offsetTop + 10,
3338 left: 0,
3339 behavior: 'smooth'
3340 });
3341
3342 //check if icon should be active
3343 checkActiveAction( type );
3344 initiateMultiselect();
3345 showInputIcon( '#frm_form_action_' + actionId );
3346 }
3347 });
3348 }
3349
3350 function toggleActionGroups() {
3351 /*jshint validthis:true */
3352 var actions = document.getElementById( 'frm_email_addon_menu' ).classList,
3353 search = document.getElementById( 'actions-search-input' );
3354
3355 if ( actions.contains( 'frm-all-actions' ) ) {
3356 actions.remove( 'frm-all-actions' );
3357 actions.add( 'frm-limited-actions' );
3358 } else {
3359 actions.add( 'frm-all-actions' );
3360 actions.remove( 'frm-limited-actions' );
3361 }
3362
3363 // Reset search.
3364 search.value = '';
3365 triggerEvent( search, 'input' );
3366 }
3367
3368 function getNewActionId() {
3369 var len = 0;
3370 if ( jQuery( '.frm_form_action_settings:last' ).length ) {
3371 //Get number of previous action
3372 len = jQuery( '.frm_form_action_settings:last' ).attr( 'id' ).replace( 'frm_form_action_', '' );
3373 }
3374 len = parseInt( len, 10 ) + 1;
3375 if ( typeof document.getElementById( 'frm_form_action_' + len ) !== 'undefined' ) {
3376 len = len + 100;
3377 }
3378 return len;
3379 }
3380
3381 function clickAction( obj ) {
3382 var $thisobj = jQuery( obj );
3383
3384 if ( obj.className.indexOf( 'selected' ) !== -1 ) {
3385 return;
3386 }
3387 if ( obj.className.indexOf( 'edit_field_type_end_divider' ) !== -1 && $thisobj.closest( '.edit_field_type_divider' ).hasClass( 'no_repeat_section' ) ) {
3388 return;
3389 }
3390
3391 deselectFields();
3392 $thisobj.addClass( 'selected' );
3393
3394 showFieldOptions( obj );
3395 }
3396
3397 /**
3398 * When a field is selected, show the field settings in the sidebar.
3399 */
3400 function showFieldOptions( obj ) {
3401 var i, singleField,
3402 fieldId = obj.getAttribute( 'data-fid' ),
3403 fieldType = obj.getAttribute( 'data-type' ),
3404 allFieldSettings = document.querySelectorAll( '.frm-single-settings:not(.frm_hidden)' );
3405
3406 for ( i = 0; i < allFieldSettings.length; i++ ) {
3407 allFieldSettings[i].classList.add( 'frm_hidden' );
3408 }
3409
3410 singleField = document.getElementById( 'frm-single-settings-' + fieldId );
3411 moveFieldSettings( singleField );
3412
3413 if ( fieldType && 'quantity' === fieldType ) {
3414 popProductFields( jQuery( singleField ).find( '.frmjs_prod_field_opt' )[0]);
3415 }
3416
3417 singleField.classList.remove( 'frm_hidden' );
3418 document.getElementById( 'frm-options-panel-tab' ).click();
3419 }
3420
3421 /**
3422 * Move the settings to the sidebar the first time they are changed or selected.
3423 * Keep the end marker at the end of the form.
3424 */
3425 function moveFieldSettings( singleField ) {
3426 if ( singleField === null ) {
3427 // The field may have not been loaded yet via ajax.
3428 return;
3429 }
3430
3431 var classes = singleField.parentElement.classList;
3432 if ( classes.contains( 'frm_field_box' ) || classes.contains( 'divider_section_only' ) ) {
3433 var endMarker = document.getElementById( 'frm-end-form-marker' );
3434 builderForm.insertBefore( singleField, endMarker );
3435 }
3436 }
3437
3438 function showEmailRow() {
3439 /*jshint validthis:true */
3440 var actionKey = jQuery( this ).closest( '.frm_form_action_settings' ).data( 'actionkey' );
3441 var rowType = this.getAttribute( 'data-emailrow' );
3442
3443 jQuery( '#frm_form_action_' + actionKey + ' .frm_' + rowType + '_row' ).fadeIn( 'slow' );
3444 jQuery( this ).fadeOut( 'slow' );
3445 }
3446
3447 function hideEmailRow() {
3448 /*jshint validthis:true */
3449 var actionBox = jQuery( this ).closest( '.frm_form_action_settings' ),
3450 rowType = this.getAttribute( 'data-emailrow' ),
3451 emailRowSelector = '.frm_' + rowType + '_row',
3452 emailButtonSelector = '.frm_' + rowType + '_button';
3453
3454 jQuery( actionBox ).find( emailButtonSelector ).fadeIn( 'slow' );
3455 jQuery( actionBox ).find( emailRowSelector ).fadeOut( 'slow', function() {
3456 jQuery( actionBox ).find( emailRowSelector + ' input' ).val( '' );
3457 });
3458 }
3459
3460 function showEmailWarning() {
3461 /*jshint validthis:true */
3462 var actionBox = jQuery( this ).closest( '.frm_form_action_settings' ),
3463 emailRowSelector = '.frm_from_to_match_row',
3464 fromVal = actionBox.find( 'input[name$="[post_content][from]"]' ).val(),
3465 toVal = actionBox.find( 'input[name$="[post_content][email_to]"]' ).val();
3466
3467 if ( fromVal === toVal ) {
3468 jQuery( actionBox ).find( emailRowSelector ).fadeIn( 'slow' );
3469 } else {
3470 jQuery( actionBox ).find( emailRowSelector ).fadeOut( 'slow' );
3471 }
3472 }
3473
3474 function checkActiveAction( type ) {
3475 var limit = parseInt( jQuery( '.frm_' + type + '_action' ).data( 'limit' ), 10 );
3476 var len = jQuery( '.frm_single_' + type + '_settings' ).length;
3477 var limitClass;
3478 if ( len >= limit ) {
3479 limitClass = 'frm_inactive_action';
3480 limitClass += ( limit > 0 ) ? ' frm_already_used' : '';
3481 jQuery( '.frm_' + type + '_action' ).removeClass( 'frm_active_action' ).addClass( limitClass );
3482 } else {
3483 jQuery( '.frm_' + type + '_action' ).removeClass( 'frm_inactive_action frm_already_used' ).addClass( 'frm_active_action' );
3484 }
3485 }
3486
3487 function onlyOneActionMessage() {
3488 infoModal( frm_admin_js.only_one_action );
3489 }
3490
3491 function addFormLogicRow() {
3492 /*jshint validthis:true */
3493 var id = jQuery( this ).data( 'emailkey' ),
3494 type = jQuery( this ).closest( '.frm_form_action_settings' ).find( '.frm_action_name' ).val(),
3495 metaName = 0,
3496 formId = document.getElementById( 'form_id' ).value;
3497 if ( jQuery( '#frm_form_action_' + id + ' .frm_logic_row' ).length ) {
3498 metaName = 1 + parseInt( jQuery( '#frm_form_action_' + id + ' .frm_logic_row:last' ).attr( 'id' ).replace( 'frm_logic_' + id + '_', '' ), 10 );
3499 }
3500 jQuery.ajax({
3501 type: 'POST', url: ajaxurl,
3502 data: {
3503 action: 'frm_add_form_logic_row',
3504 email_id: id,
3505 form_id: formId,
3506 meta_name: metaName,
3507 type: type,
3508 nonce: frmGlobal.nonce
3509 },
3510 success: function( html ) {
3511 jQuery( document.getElementById( 'logic_link_' + id ) ).fadeOut( 'slow', function() {
3512 var $logicRow = jQuery( document.getElementById( 'frm_logic_row_' + id ) );
3513 $logicRow.append( html );
3514 $logicRow.parent( '.frm_logic_rows' ).fadeIn( 'slow' );
3515 });
3516 }
3517 });
3518 return false;
3519 }
3520
3521 function toggleSubmitLogic() {
3522 /*jshint validthis:true */
3523 if ( this.checked ) {
3524 addSubmitLogic();
3525 } else {
3526 jQuery( '.frm_logic_row_submit' ).remove();
3527 document.getElementById( 'frm_submit_logic_rows' ).style.display = 'none';
3528 }
3529 }
3530
3531 /**
3532 * Adds submit button Conditional Logic row and reveals submit button Conditional Logic
3533 *
3534 * @returns {boolean}
3535 */
3536 function addSubmitLogic() {
3537 /*jshint validthis:true */
3538 var last,
3539 formId = thisFormId,
3540 metaName = 0;
3541 if ( jQuery( '#frm_submit_logic_row .frm_logic_row' ).length > 0 ) {
3542 last = jQuery( '#frm_submit_logic_row .frm_logic_row:last' );
3543
3544 metaName = 1 + parseInt( last.attr( 'id' ).replace( 'frm_logic_submit_', '' ), 10 );
3545 }
3546 jQuery.ajax({
3547 type: 'POST',
3548 url: ajaxurl,
3549 data: {
3550 action: 'frm_add_submit_logic_row',
3551 form_id: formId,
3552 meta_name: metaName,
3553 nonce: frmGlobal.nonce
3554 },
3555 success: function( html ) {
3556 var $logicRow = jQuery( document.getElementById( 'frm_submit_logic_row' ) );
3557 $logicRow.append( html );
3558 $logicRow.parent( '.frm_submit_logic_rows' ).fadeIn( 'slow' );
3559 }
3560 });
3561 return false;
3562 }
3563
3564 /**
3565 * When the user selects a field for a submit condition, update corresponding options field accordingly.
3566 */
3567 function addSubmitLogicOpts() {
3568 var fieldOpt = jQuery( this );
3569 var fieldId = fieldOpt.find( ':selected' ).val();
3570
3571 if ( fieldId ) {
3572 var row = fieldOpt.data( 'row' );
3573 frmGetFieldValues( fieldId, 'submit', row, '', 'options[submit_conditions][hide_opt][]' );
3574 }
3575 }
3576
3577 function formatEmailSetting() {
3578 /*jshint validthis:true */
3579 /*var val = jQuery( this ).val();
3580 var email = val.match( /(\s[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+)/gi );
3581 if(email !== null && email.length) {
3582 //has email
3583 //TODO: add < > if they aren't there
3584 }*/
3585 }
3586
3587 function maybeShowFormMessages() {
3588 var header = document.getElementById( 'frm_messages_header' );
3589 if ( showFormMessages() ) {
3590 header.style.display = 'block';
3591 } else {
3592 header.style.display = 'none';
3593 }
3594 }
3595
3596 function showFormMessages() {
3597 var action = document.getElementById( 'success_action' );
3598 var selectedAction = action.options[action.selectedIndex].value;
3599 if ( selectedAction === 'message' ) {
3600 return true;
3601 }
3602
3603 var show = false;
3604 var editable = document.getElementById( 'editable' );
3605 if ( editable !== null ) {
3606 show = editable.checked && jQuery( document.getElementById( 'edit_action' ) ).val() === 'message';
3607 if ( ! show ) {
3608 show = document.getElementById( 'save_draft' ).checked;
3609 }
3610 }
3611 return show;
3612 }
3613
3614 function checkDupPost() {
3615 /*jshint validthis:true */
3616 var postField = jQuery( 'select.frm_single_post_field' );
3617 postField.css( 'border-color', '' );
3618 var $t = this;
3619 var v = jQuery( $t ).val();
3620 if ( v === '' || v === 'checkbox' ) {
3621 return false;
3622 }
3623 postField.each( function() {
3624 if ( jQuery( this ).val() === v && this.name !== $t.name ) {
3625 this.style.borderColor = 'red';
3626 jQuery( $t ).val( '' );
3627 infoModal( 'Oops. You have already used that field.' );
3628 return false;
3629 }
3630 });
3631 }
3632
3633 function togglePostContent() {
3634 /*jshint validthis:true */
3635 var v = jQuery( this ).val();
3636 if ( '' === v ) {
3637 jQuery( '.frm_post_content_opt, select.frm_dyncontent_opt' ).hide().val( '' );
3638 jQuery( '.frm_dyncontent_opt' ).hide();
3639 } else if ( 'post_content' === v ) {
3640 jQuery( '.frm_post_content_opt' ).show();
3641 jQuery( '.frm_dyncontent_opt' ).hide();
3642 jQuery( 'select.frm_dyncontent_opt' ).val( '' );
3643 } else {
3644 jQuery( '.frm_post_content_opt' ).hide().val( '' );
3645 jQuery( 'select.frm_dyncontent_opt, .frm_form_field.frm_dyncontent_opt' ).show();
3646 }
3647 }
3648
3649 function fillDyncontent() {
3650 /*jshint validthis:true */
3651 var v = jQuery( this ).val();
3652 var $dyn = jQuery( document.getElementById( 'frm_dyncontent' ) );
3653 if ( '' === v || 'new' === v ) {
3654 $dyn.val( '' );
3655 jQuery( '.frm_dyncontent_opt' ).show();
3656 } else {
3657 jQuery.ajax({
3658 type: 'POST', url: ajaxurl,
3659 data: {action: 'frm_display_get_content', id: v, nonce: frmGlobal.nonce},
3660 success: function( val ) {
3661 $dyn.val( val );
3662 jQuery( '.frm_dyncontent_opt' ).show();
3663 }
3664 });
3665 }
3666 }
3667
3668 function switchPostType() {
3669 /*jshint validthis:true */
3670 // update all rows of categories/taxonomies
3671 var curSelect, newSelect,
3672 catRows = document.getElementById( 'frm_posttax_rows' ).childNodes,
3673 postType = this.value;
3674
3675 // Get new category/taxonomy options
3676 jQuery.ajax({
3677 type: 'POST',
3678 url: ajaxurl,
3679 data: {
3680 action: 'frm_replace_posttax_options',
3681 post_type: postType,
3682 nonce: frmGlobal.nonce
3683 },
3684 success: function( html ) {
3685
3686 // Loop through each category row, and replace the first dropdown
3687 for ( i = 0; i < catRows.length; i++ ) {
3688 // Check if current element is a div
3689 if ( catRows[i].tagName !== 'DIV' ) {
3690 continue;
3691 }
3692
3693 // Get current category select
3694 curSelect = catRows[i].getElementsByTagName( 'select' )[0];
3695
3696 // Set up new select
3697 newSelect = document.createElement( 'select' );
3698 newSelect.innerHTML = html;
3699 newSelect.className = curSelect.className;
3700 newSelect.name = curSelect.name;
3701
3702 // Replace the old select with the new select
3703 catRows[i].replaceChild( newSelect, curSelect );
3704 }
3705 }
3706 });
3707 }
3708
3709 function addPosttaxRow() {
3710 /*jshint validthis:true */
3711 addPostRow( 'tax', this );
3712 }
3713
3714 function addPostmetaRow() {
3715 /*jshint validthis:true */
3716 addPostRow( 'meta', this );
3717 }
3718
3719 function addPostRow( type, button ) {
3720 var id = jQuery( 'input[name="id"]' ).val(),
3721 settings = jQuery( button ).closest( '.frm_form_action_settings' ),
3722 key = settings.data( 'actionkey' ),
3723 postType = settings.find( '.frm_post_type' ).val(),
3724 metaName = 0;
3725
3726 if ( jQuery( '.frm_post' + type + '_row' ).length ) {
3727 var name = jQuery( '.frm_post' + type + '_row:last' ).attr( 'id' ).replace( 'frm_post' + type + '_', '' );
3728 if ( jQuery.isNumeric( name ) ) {
3729 metaName = 1 + parseInt( name, 10 );
3730 } else {
3731 metaName = 1;
3732 }
3733 }
3734 jQuery.ajax({
3735 type: 'POST', url: ajaxurl,
3736 data: {
3737 action: 'frm_add_post' + type + '_row',
3738 form_id: id,
3739 meta_name: metaName,
3740 tax_key: metaName,
3741 post_type: postType,
3742 action_key: key,
3743 nonce: frmGlobal.nonce
3744 },
3745 success: function( html ) {
3746 jQuery( document.getElementById( 'frm_post' + type + '_rows' ) ).append( html );
3747 jQuery( '.frm_add_post' + type + '_row.button' ).hide();
3748
3749 if ( type === 'meta' ) {
3750 jQuery( '.frm_name_value' ).show();
3751 jQuery( '.frm_toggle_cf_opts' ).not( ':last' ).hide();
3752 } else if ( type === 'tax' ) {
3753 jQuery( '.frm_posttax_labels' ).show();
3754 }
3755 }
3756 });
3757 }
3758
3759 function getMetaValue( id, metaName ) {
3760 var newMeta = metaName;
3761 if ( jQuery( document.getElementById( id + metaName ) ).length > 0 ) {
3762 newMeta = getMetaValue( id, metaName + 1 );
3763 }
3764 return newMeta;
3765 }
3766
3767 function changePosttaxRow() {
3768 /*jshint validthis:true */
3769 if ( ! jQuery( this ).closest( '.frm_posttax_row' ).find( '.frm_posttax_opt_list' ).length ) {
3770 return;
3771 }
3772
3773 jQuery( this ).closest( '.frm_posttax_row' ).find( '.frm_posttax_opt_list' ).html( '<div class="spinner frm_spinner" style="display:block"></div>' );
3774
3775 var postType = jQuery( this ).closest( '.frm_form_action_settings' ).find( 'select[name$="[post_content][post_type]"]' ).val(),
3776 actionKey = jQuery( this ).closest( '.frm_form_action_settings' ).data( 'actionkey' ),
3777 taxKey = jQuery( this ).closest( '.frm_posttax_row' ).attr( 'id' ).replace( 'frm_posttax_', '' ),
3778 metaName = jQuery( this ).val(),
3779 showExclude = jQuery( document.getElementById( taxKey + '_show_exclude' ) ).is( ':checked' ) ? 1 : 0,
3780 fieldId = jQuery( 'select[name$="[post_category][' + taxKey + '][field_id]"]' ).val(),
3781 id = jQuery( 'input[name="id"]' ).val();
3782
3783 jQuery.ajax({
3784 type: 'POST',
3785 url: ajaxurl,
3786 data: {
3787 action: 'frm_add_posttax_row',
3788 form_id: id,
3789 post_type: postType,
3790 tax_key: taxKey,
3791 action_key: actionKey,
3792 meta_name: metaName,
3793 field_id: fieldId,
3794 show_exclude: showExclude,
3795 nonce: frmGlobal.nonce
3796 },
3797 success: function( html ) {
3798 var $tax = jQuery( document.getElementById( 'frm_posttax_' + taxKey ) );
3799 $tax.replaceWith( html );
3800 }
3801 });
3802 }
3803
3804 function toggleCfOpts() {
3805 /*jshint validthis:true */
3806 var row = jQuery( this ).closest( '.frm_postmeta_row' );
3807 var cancel = row.find( '.frm_cancelnew' );
3808 var select = row.find( '.frm_enternew' );
3809 if ( row.find( 'select.frm_cancelnew' ).is( ':visible' ) ) {
3810 cancel.hide();
3811 select.show();
3812 } else {
3813 cancel.show();
3814 select.hide();
3815 }
3816
3817 row.find( 'input.frm_enternew, select.frm_cancelnew' ).val( '' );
3818 return false;
3819 }
3820
3821 function toggleFormOpts() {
3822 /*jshint validthis:true */
3823 var changedOpt = jQuery( this );
3824 var val = changedOpt.val();
3825 if ( changedOpt.attr( 'type' ) === 'checkbox' ) {
3826 if ( this.checked === false ) {
3827 val = '';
3828 }
3829 }
3830
3831 var toggleClass = changedOpt.data( 'toggleclass' );
3832 if ( val === '' ) {
3833 jQuery( '.' + toggleClass ).hide();
3834 } else {
3835 jQuery( '.' + toggleClass ).show();
3836 jQuery( '.hide_' + toggleClass + '_' + val ).hide();
3837 }
3838 }
3839
3840 function submitSettings() {
3841 /*jshint validthis:true */
3842 preFormSave( this );
3843 jQuery( '.frm_form_settings' ).submit();
3844 }
3845
3846 /* View Functions */
3847 function showCount() {
3848 /*jshint validthis:true */
3849 var value = jQuery( this ).val();
3850
3851 var $cont = document.getElementById( 'date_select_container' );
3852 var tab = document.getElementById( 'frm_listing_tab' );
3853 var label = tab.getAttribute( 'data-label' );
3854 if ( value === 'calendar' ) {
3855 jQuery( '.hide_dyncontent, .hide_single_content' ).removeClass( 'frm_hidden' );
3856 jQuery( '.limit_container' ).addClass( 'frm_hidden' );
3857 $cont.style.display = 'block';
3858 } else if ( value === 'dynamic' ) {
3859 jQuery( '.hide_dyncontent, .limit_container, .hide_single_content' ).removeClass( 'frm_hidden' );
3860 } else if ( value === 'one' ) {
3861 label = tab.getAttribute( 'data-one' );
3862 jQuery( '.hide_dyncontent, .limit_container, .hide_single_content' ).addClass( 'frm_hidden' );
3863 } else {
3864 jQuery( '.hide_dyncontent' ).addClass( 'frm_hidden' );
3865 jQuery( '.limit_container, .hide_single_content' ).removeClass( 'frm_hidden' );
3866 }
3867
3868 if ( value !== 'calendar' ) {
3869 $cont.style.display = 'none';
3870 }
3871 tab.innerHTML = label;
3872 }
3873
3874 function displayFormSelected() {
3875 /*jshint validthis:true */
3876 var formId = jQuery( this ).val();
3877 thisFormId = formId; // set the global form id
3878 if ( formId === '' ) {
3879 return;
3880 }
3881
3882 jQuery.ajax({
3883 type: 'POST',
3884 url: ajaxurl,
3885 data: {
3886 action: 'frm_get_cd_tags_box',
3887 form_id: formId,
3888 nonce: frmGlobal.nonce
3889 },
3890 success: function( html ) {
3891 jQuery( '#frm_adv_info .categorydiv' ).html( html );
3892 }
3893 });
3894
3895 jQuery.ajax({
3896 type: 'POST',
3897 url: ajaxurl,
3898 data: {
3899 action: 'frm_get_date_field_select',
3900 form_id: formId,
3901 nonce: frmGlobal.nonce
3902 },
3903 success: function( html ) {
3904 jQuery( document.getElementById( 'date_select_container' ) ).html( html );
3905 }
3906 });
3907 }
3908
3909 function clickTabsAfterAjax() {
3910 /*jshint validthis:true */
3911 var t = jQuery( this ).attr( 'href' );
3912 jQuery( this ).parent().addClass( 'tabs' ).siblings( 'li' ).removeClass( 'tabs' );
3913 jQuery( t ).show().siblings( '.tabs-panel' ).hide();
3914 return false;
3915 }
3916
3917 function clickContentTab() {
3918 /*jshint validthis:true */
3919 link = jQuery( this );
3920 var t = link.attr( 'href' );
3921 if ( typeof t === 'undefined' ) {
3922 return false;
3923 }
3924
3925 var c = t.replace( '#', '.' );
3926 link.closest( '.nav-tab-wrapper' ).find( 'a' ).removeClass( 'nav-tab-active' );
3927 link.addClass( 'nav-tab-active' );
3928 jQuery( '.nav-menu-content' ).not( t ).not( c ).hide();
3929 jQuery( t + ',' + c ).show();
3930
3931 return false;
3932 }
3933
3934 function addOrderRow() {
3935 var l = 0;
3936 if ( jQuery( '#frm_order_options .frm_logic_rows div:last' ).length > 0 ) {
3937 l = jQuery( '#frm_order_options .frm_logic_rows div:last' ).attr( 'id' ).replace( 'frm_order_field_', '' );
3938 }
3939 jQuery.ajax({
3940 type: 'POST', url: ajaxurl,
3941 data: {
3942 action: 'frm_add_order_row',
3943 form_id: thisFormId,
3944 order_key: ( parseInt( l, 10 ) + 1 ),
3945 nonce: frmGlobal.nonce
3946 },
3947 success: function( html ) {
3948 jQuery( '#frm_order_options .frm_logic_rows' ).append( html ).show().prev( '.frm_add_order_row' ).hide();
3949 }
3950 });
3951 }
3952
3953 function addWhereRow() {
3954 var l = 0;
3955 if ( jQuery( '#frm_where_options .frm_logic_rows div:last' ).length ) {
3956 l = jQuery( '#frm_where_options .frm_logic_rows div:last' ).attr( 'id' ).replace( 'frm_where_field_', '' );
3957 }
3958 jQuery.ajax({
3959 type: 'POST', url: ajaxurl,
3960 data: {
3961 action: 'frm_add_where_row',
3962 form_id: thisFormId,
3963 where_key: ( parseInt( l, 10 ) + 1 ),
3964 nonce: frmGlobal.nonce
3965 },
3966 success: function( html ) {
3967 jQuery( '#frm_where_options .frm_logic_rows' ).append( html ).show().prev( '.frm_add_where_row' ).hide();
3968 }
3969 });
3970 }
3971
3972 function insertWhereOptions() {
3973 /*jshint validthis:true */
3974 var value = this.value,
3975 whereKey = jQuery( this ).closest( '.frm_where_row' ).attr( 'id' ).replace( 'frm_where_field_', '' );
3976
3977 jQuery.ajax({
3978 type: 'POST',
3979 url: ajaxurl,
3980 data: {
3981 action: 'frm_add_where_options',
3982 where_key: whereKey,
3983 field_id: value,
3984 nonce: frmGlobal.nonce
3985 },
3986 success: function( html ) {
3987 jQuery( document.getElementById( 'where_field_options_' + whereKey ) ).html( html );
3988 }
3989 });
3990 }
3991
3992 function hideWhereOptions() {
3993 /*jshint validthis:true */
3994 var value = this.value,
3995 whereKey = jQuery( this ).closest( '.frm_where_row' ).attr( 'id' ).replace( 'frm_where_field_', '' );
3996
3997 if ( value === 'group_by' || value === 'group_by_newest' ) {
3998 document.getElementById( 'where_field_options_' + whereKey ).style.display = 'none';
3999 } else {
4000 document.getElementById( 'where_field_options_' + whereKey ).style.display = 'inline-block';
4001 }
4002 }
4003
4004 function setDefaultPostStatus() {
4005 var urlQuery = window.location.search.substring( 1 );
4006 if ( urlQuery.indexOf( 'action=edit' ) === -1 ) {
4007 document.getElementById( 'post-visibility-display' ).innerHTML = frm_admin_js.private_label;
4008 document.getElementById( 'hidden-post-visibility' ).value = 'private';
4009 document.getElementById( 'visibility-radio-private' ).checked = true;
4010 }
4011 }
4012
4013 /* Customization Panel */
4014 function insertCode( e ) {
4015 /*jshint validthis:true */
4016 e.preventDefault();
4017 insertFieldCode( jQuery( this ), this.getAttribute( 'data-code' ) );
4018 return false;
4019 }
4020
4021 function insertFieldCode( element, variable ) {
4022 var rich = false,
4023 elementId = element;
4024 if ( typeof element === 'object' ) {
4025 if ( element.hasClass( 'frm_noallow' ) ) {
4026 return;
4027 }
4028
4029 elementId = jQuery( element ).closest( '[data-fills]' ).attr( 'data-fills' );
4030 if ( typeof elementId === 'undefined' ) {
4031 elementId = element.closest( 'div' ).attr( 'class' );
4032 if ( typeof elementId !== 'undefined' ) {
4033 elementId = elementId.split( ' ' )[1];
4034 }
4035 }
4036 }
4037
4038 if ( typeof elementId === 'undefined' ) {
4039 var active = document.activeElement;
4040 if ( active.type === 'search' ) {
4041 // If the search field has focus, find the correct field.
4042 elementId = active.id.replace( '-search-input', '' );
4043 if ( elementId.match( /\d/gi ) === null ) {
4044 active = jQuery( '.frm-single-settings:visible .' + elementId );
4045 elementId = active.attr( 'id' );
4046 }
4047 } else {
4048 elementId = active.id;
4049 }
4050 }
4051
4052 if ( elementId ) {
4053 rich = jQuery( '#wp-' + elementId + '-wrap.wp-editor-wrap' ).length > 0;
4054 }
4055
4056 var contentBox = jQuery( document.getElementById( elementId ) );
4057 if ( typeof element.attr( 'data-shortcode' ) === 'undefined' && ( ! contentBox.length || typeof contentBox.attr( 'data-shortcode' ) === 'undefined' ) ) {
4058 // this helps to exclude those that don't want shortcode-like inserted content e.g. frm-pro's summary field
4059 var doShortcode = element.parents( 'ul.frm_code_list' ).attr( 'data-shortcode' );
4060 if ( doShortcode === 'undefined' || doShortcode !== 'no' ) {
4061 variable = '[' + variable + ']';
4062 }
4063 }
4064
4065 if ( rich ) {
4066 wpActiveEditor = elementId;
4067 send_to_editor( variable );
4068 return;
4069 }
4070
4071 if ( ! contentBox.length ) {
4072 return false;
4073 }
4074
4075 if ( variable === '[default-html]' || variable === '[default-plain]' ) {
4076 var p = 0;
4077 if ( variable === '[default-plain]' ) {
4078 p = 1;
4079 }
4080 jQuery.ajax({
4081 type: 'POST', url: ajaxurl,
4082 data: {
4083 action: 'frm_get_default_html',
4084 form_id: jQuery( 'input[name="id"]' ).val(),
4085 plain_text: p,
4086 nonce: frmGlobal.nonce
4087 },
4088 success: function( msg ) {
4089 insertContent( contentBox, msg );
4090 }
4091 });
4092 } else {
4093 insertContent( contentBox, variable );
4094 }
4095 return false;
4096 }
4097
4098 function insertContent( contentBox, variable ) {
4099 if ( document.selection ) {
4100 contentBox[0].focus();
4101 document.selection.createRange().text = variable;
4102 } else {
4103 obj = contentBox[0];
4104 var e = obj.selectionEnd;
4105
4106 variable = maybeFormatInsertedContent( contentBox, variable, obj.selectionStart, e );
4107
4108 obj.value = obj.value.substr( 0, obj.selectionStart ) + variable + obj.value.substr( obj.selectionEnd, obj.value.length );
4109 var s = e + variable.length;
4110 obj.focus();
4111 obj.setSelectionRange( s, s );
4112 }
4113 contentBox.change(); //trigger change
4114 }
4115
4116 function maybeFormatInsertedContent( input, textToInsert, selectionStart, selectionEnd ) {
4117 var separator = input.data( 'sep' );
4118 if ( undefined === separator ) {
4119 return textToInsert;
4120 }
4121
4122 var value = input.val();
4123
4124 if ( ! value.trim().length ) {
4125 return textToInsert;
4126 }
4127
4128 var startPattern = new RegExp( separator + '\\s*$' );
4129 var endPattern = new RegExp( '^\\s*' + separator );
4130
4131 if ( value.substr( 0, selectionStart ).trim().length && false === startPattern.test( value.substr( 0, selectionStart ) ) ) {
4132 textToInsert = separator + textToInsert;
4133 }
4134
4135 if ( value.substr( selectionEnd, value.length ).trim().length && false === endPattern.test( value.substr( selectionEnd, value.length ) ) ) {
4136 textToInsert += separator;
4137 }
4138
4139 return textToInsert;
4140 }
4141
4142 function resetLogicBuilder() {
4143 /*jshint validthis:true */
4144 var id = document.getElementById( 'frm-id-condition' ),
4145 key = document.getElementById( 'frm-key-condition' );
4146
4147 if ( this.checked ) {
4148 id.classList.remove( 'frm_hidden' );
4149 key.classList.add( 'frm_hidden' );
4150 triggerEvent( key, 'change' );
4151 } else {
4152 id.classList.add( 'frm_hidden' );
4153 key.classList.remove( 'frm_hidden' );
4154 triggerEvent( id, 'change' );
4155 }
4156 }
4157
4158 function setLogicExample() {
4159 var field, code,
4160 idKey = document.getElementById( 'frm-id-key-condition' ).checked ? 'frm-id-condition' : 'frm-key-condition',
4161 is = document.getElementById( 'frm-is-condition' ).value,
4162 text = document.getElementById( 'frm-text-condition' ).value,
4163 result = document.getElementById( 'frm-insert-condition' );
4164
4165 idKey = document.getElementById( idKey );
4166 field = idKey.options[idKey.selectedIndex].value;
4167 code = 'if ' + field + ' ' + is + '="' + text + '"]';
4168 result.setAttribute( 'data-code', code + frm_admin_js.conditional_text + '[/if ' + field );
4169 result.innerHTML = '[' + code + '[/if ' + field + ']';
4170 }
4171
4172 function showBuilderModal() {
4173 /*jshint validthis:true */
4174 var moreIcon = getIconForInput( this );
4175 showInlineModal( moreIcon, this );
4176 }
4177
4178 function maybeShowModal( input ) {
4179 var moreIcon;
4180 if ( input.parentNode.parentNode.classList.contains( 'frm_has_shortcodes' ) ) {
4181 hideShortcodes();
4182 moreIcon = getIconForInput( input );
4183 if ( moreIcon.tagName === 'use' ) {
4184 moreIcon = moreIcon.firstElementChild;
4185 if ( moreIcon.getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' ).indexOf( 'frm_close_icon' ) === -1 ) {
4186 showShortcodeBox( moreIcon, 'nofocus' );
4187 }
4188 } else if ( ! moreIcon.classList.contains( 'frm_close_icon' ) ) {
4189 showShortcodeBox( moreIcon, 'nofocus' );
4190 }
4191 }
4192 }
4193
4194 function showShortcodes( e ) {
4195 /*jshint validthis:true */
4196 e.preventDefault();
4197 e.stopPropagation();
4198
4199 showShortcodeBox( this );
4200 }
4201
4202 function showShortcodeBox( moreIcon, shouldFocus ) {
4203 var pos = moreIcon.getBoundingClientRect(),
4204 input = getInputForIcon( moreIcon ),
4205 box = document.getElementById( 'frm_adv_info' ),
4206 classes = moreIcon.className,
4207 parentPos = box.parentElement.getBoundingClientRect();
4208
4209 if ( moreIcon.tagName === 'svg' ) {
4210 moreIcon = moreIcon.firstElementChild;
4211 }
4212 if ( moreIcon.tagName === 'use' ) {
4213 classes = moreIcon.getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' );
4214 }
4215
4216 if ( classes.indexOf( 'frm_close_icon' ) !== -1 ) {
4217 hideShortcodes( box );
4218 } else {
4219 box.style.top = ( pos.top - parentPos.top + 32 ) + 'px';
4220 box.style.left = ( pos.left - parentPos.left - 257 ) + 'px';
4221
4222 jQuery( '.frm_code_list a' ).removeClass( 'frm_noallow' );
4223 if ( input.classList.contains( 'frm_not_email_to' ) ) {
4224 jQuery( '#frm-insert-fields-box .frm_code_list li:not(.show_frm_not_email_to) a' ).addClass( 'frm_noallow' );
4225 } else if ( input.classList.contains( 'frm_not_email_subject' ) ) {
4226 jQuery( '.frm_code_list li.hide_frm_not_email_subject a' ).addClass( 'frm_noallow' );
4227 }
4228
4229 box.setAttribute( 'data-fills', input.id );
4230 box.style.display = 'block';
4231
4232 if ( moreIcon.tagName === 'use' ) {
4233 moreIcon.setAttributeNS( 'http://www.w3.org/1999/xlink', 'href', '#frm_close_icon' );
4234 } else {
4235 moreIcon.className = classes.replace( 'frm_more_horiz_solid_icon', 'frm_close_icon' );
4236 }
4237
4238 if ( shouldFocus !== 'nofocus' ) {
4239 input.focus();
4240 }
4241 }
4242 }
4243
4244 /**
4245 * Get the input box for the selected ... icon.
4246 */
4247 function getInputForIcon( moreIcon ) {
4248 var input = moreIcon.nextElementSibling;
4249 if ( input !== null && input.tagName !== 'INPUT' && input.tagName !== 'TEXTAREA' ) {
4250 // Workaround for 1Password.
4251 input = input.nextElementSibling;
4252 }
4253 return input;
4254 }
4255
4256 /**
4257 * Get the ... icon for the selected input box.
4258 */
4259 function getIconForInput( input ) {
4260 var moreIcon = input.previousElementSibling;
4261 if ( moreIcon !== null && moreIcon.tagName !== 'I' && moreIcon.tagName !== 'svg' ) {
4262 moreIcon = moreIcon.previousElementSibling;
4263 }
4264 return moreIcon;
4265 }
4266
4267 function hideShortcodes( box ) {
4268 var i, u, closeIcons, closeSvg;
4269 if ( typeof box === 'undefined' ) {
4270 box = document.getElementById( 'frm_adv_info' );
4271 if ( box === null ) {
4272 return;
4273 }
4274 }
4275
4276 if ( document.getElementById( 'frm_dyncontent' ) !== null ) {
4277 // Don't run when in the sidebar.
4278 return;
4279 }
4280
4281 box.style.display = 'none';
4282
4283 closeIcons = document.querySelectorAll( '.frm-show-box.frm_close_icon' );
4284 for ( i = 0; i < closeIcons.length; i++ ) {
4285 closeIcons[i].classList.remove( 'frm_close_icon' );
4286 closeIcons[i].classList.add( 'frm_more_horiz_solid_icon' );
4287 }
4288
4289 closeSvg = document.querySelectorAll( '.frm_has_shortcodes use' );
4290 for ( u = 0; u < closeSvg.length; u++ ) {
4291 if ( closeSvg[u].getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' ) === '#frm_close_icon' ) {
4292 closeSvg[u].setAttributeNS( 'http://www.w3.org/1999/xlink', 'href', '#frm_more_horiz_solid_icon' );
4293 }
4294 }
4295 }
4296
4297 function initToggleShortcodes() {
4298 if ( typeof tinymce !== 'object' ) {
4299 return;
4300 }
4301
4302 DOM = tinymce.DOM;
4303 if ( typeof DOM.events !== 'undefined' && typeof DOM.events.add !== 'undefined' ) {
4304 DOM.events.add( DOM.select( '.wp-editor-wrap' ), 'mouseover', function() {
4305 if ( jQuery( '*:focus' ).length > 0 ) {
4306 return;
4307 }
4308 if ( this.id ) {
4309 toggleAllowedShortcodes( this.id.slice( 3, -5 ), 'focusin' );
4310 }
4311 });
4312 DOM.events.add( DOM.select( '.wp-editor-wrap' ), 'mouseout', function() {
4313 if ( jQuery( '*:focus' ).length > 0 ) {
4314 return;
4315 }
4316 if ( this.id ) {
4317 toggleAllowedShortcodes( this.id.slice( 3, -5 ), 'focusin' );
4318 }
4319 });
4320 } else {
4321 jQuery( '#frm_dyncontent' ).on( 'mouseover mouseout', '.wp-editor-wrap', function() {
4322 if ( jQuery( '*:focus' ).length > 0 ) {
4323 return;
4324 }
4325 if ( this.id ) {
4326 toggleAllowedShortcodes( this.id.slice( 3, -5 ), 'focusin' );
4327 }
4328 });
4329 }
4330 }
4331
4332 function toggleAllowedShortcodes( id ) {
4333 var c, clickedID;
4334 if ( typeof id === 'undefined' ) {
4335 id = '';
4336 }
4337 c = id;
4338
4339 if ( id.indexOf( '-search-input' ) !== -1 ) {
4340 return;
4341 }
4342
4343 if ( id !== '' ) {
4344 var $ele = jQuery( document.getElementById( id ) );
4345 if ( $ele.attr( 'class' ) && id !== 'wpbody-content' && id !== 'content' && id !== 'dyncontent' && id !== 'success_msg' ) {
4346 var d = $ele.attr( 'class' ).split( ' ' )[0];
4347 if ( d === 'frm_long_input' || d === 'frm_98_width' || typeof d === 'undefined' ) {
4348 d = '';
4349 } else {
4350 id = jQuery.trim( d );
4351 }
4352 c = c + ' ' + d;
4353 c = c.replace( 'widefat', '' ).replace( 'frm_with_left_label', '' );
4354 }
4355 }
4356
4357 jQuery( '#frm-insert-fields-box,#frm-conditionals,#frm-adv-info-tab,#frm-dynamic-values' ).attr( 'data-fills', jQuery.trim( c ) );
4358 var a = [
4359 'content', 'wpbody-content', 'dyncontent', 'success_url',
4360 'success_msg', 'edit_msg', 'frm_dyncontent', 'frm_not_email_message',
4361 'frm_not_email_subject'
4362 ];
4363 var b = [
4364 'before_content', 'after_content', 'frm_not_email_to',
4365 'dyn_default_value'
4366 ];
4367
4368 if ( jQuery.inArray( id, a ) >= 0 ) {
4369 jQuery( '.frm_code_list a' ).removeClass( 'frm_noallow' ).addClass( 'frm_allow' );
4370 jQuery( '.frm_code_list a.hide_' + id ).addClass( 'frm_noallow' ).removeClass( 'frm_allow' );
4371 } else if ( jQuery.inArray( id, b ) >= 0 ) {
4372 jQuery( '.frm_code_list:not(.frm-dropdown-menu) a:not(.show_' + id + ')' ).addClass( 'frm_noallow' ).removeClass( 'frm_allow' );
4373 jQuery( '.frm_code_list a.show_' + id ).removeClass( 'frm_noallow' ).addClass( 'frm_allow' );
4374 } else {
4375 jQuery( '.frm_code_list:not(.frm-dropdown-menu) a' ).addClass( 'frm_noallow' ).removeClass( 'frm_allow' );
4376 }
4377
4378 // Automatically select a tab.
4379 if ( id === 'dyn_default_value' ) {
4380 clickedID = 'frm_dynamic_values';
4381 jQuery( document.getElementById( clickedID + '_tab' ) ).click();
4382 jQuery( '#' + clickedID.replace( /_/g, '-' ) + ' .frm_show_inactive' ).addClass( 'frm_hidden' );
4383 jQuery( '#' + clickedID.replace( /_/g, '-' ) + ' .frm_show_active' ).removeClass( 'frm_hidden' );
4384 }
4385 }
4386
4387 function toggleAllowedHTML( input ) {
4388 var b,
4389 id = input.id;
4390 if ( typeof id === 'undefined' || id.indexOf( '-search-input' ) !== -1 ) {
4391 return;
4392 }
4393
4394 jQuery( '#frm-adv-info-tab' ).attr( 'data-fills', jQuery.trim( id ) );
4395 if ( input.classList.contains( 'field_custom_html' ) ) {
4396 id = 'field_custom_html';
4397 }
4398
4399 b = [ 'after_html', 'before_html', 'submit_html', 'field_custom_html' ];
4400 if ( jQuery.inArray( id, b ) >= 0 ) {
4401 jQuery( '.frm_code_list li:not(.show_' + id + ')' ).addClass( 'frm_hidden' );
4402 jQuery( '.frm_code_list li.show_' + id ).removeClass( 'frm_hidden' );
4403 }
4404 }
4405
4406 function toggleKeyID( switchTo, e ) {
4407 e.stopPropagation();
4408 jQuery( '.frm_code_list .frmids, .frm_code_list .frmkeys' ).addClass( 'frm_hidden' );
4409 jQuery( '.frm_code_list .' + switchTo ).removeClass( 'frm_hidden' );
4410 jQuery( '.frmids, .frmkeys' ).removeClass( 'current' );
4411 jQuery( '.' + switchTo ).addClass( 'current' );
4412 }
4413
4414 /* Styling */
4415
4416 //function to append a new theme stylesheet with the new style changes
4417 function updateUICSS( locStr ) {
4418 if ( locStr == -1 ) {
4419 jQuery( 'link.ui-theme' ).remove();
4420 return false;
4421 }
4422 var cssLink = jQuery( '<link href="' + locStr + '" type="text/css" rel="Stylesheet" class="ui-theme" />' );
4423 jQuery( 'head' ).append( cssLink );
4424
4425 if ( jQuery( 'link.ui-theme' ).length > 1 ) {
4426 jQuery( 'link.ui-theme:first' ).remove();
4427 }
4428 }
4429
4430 function setPosClass() {
4431 /*jshint validthis:true */
4432 var value = this.value;
4433 if ( value === 'none' ) {
4434 value = 'top';
4435 } else if ( value === 'no_label' ) {
4436 value = 'none';
4437 }
4438 jQuery( '.frm_pos_container' ).removeClass( 'frm_top_container frm_left_container frm_right_container frm_none_container frm_inside_container' ).addClass( 'frm_' + value + '_container' );
4439 }
4440
4441 function collapseAllSections() {
4442 jQuery( '.control-section.accordion-section.open' ).removeClass( 'open' );
4443 }
4444
4445 function textSquishCheck() {
4446 var size = document.getElementById( 'frm_field_font_size' ).value.replace( /\D/g, '' );
4447 var height = document.getElementById( 'frm_field_height' ).value.replace( /\D/g, '' );
4448 var paddingEntered = document.getElementById( 'frm_field_pad' ).value.split( ' ' );
4449 var paddingCount = paddingEntered.length;
4450
4451 // If too many or too few padding entries, leave now
4452 if ( paddingCount === 0 || paddingCount > 4 || height === '' ) {
4453 return;
4454 }
4455
4456 // Get the top and bottom padding from entered values
4457 var paddingTop = paddingEntered[0].replace( /\D/g, '' );
4458 var paddingBottom = paddingTop;
4459 if ( paddingCount >= 3 ) {
4460 paddingBottom = paddingEntered[2].replace( /\D/g, '' );
4461 }
4462
4463 // Check if there is enough space for text
4464 var textSpace = height - size - paddingTop - paddingBottom - 3;
4465 if ( textSpace < 0 ) {
4466 infoModal( frm_admin_js.css_invalid_size );
4467 }
4468 }
4469
4470 /* Global settings page */
4471 function loadSettingsTab( anchor ) {
4472 var holder = anchor.replace( '#', '' );
4473 var holderContainer = jQuery( '.frm_' + holder + '_ajax' );
4474 if ( holderContainer.length ) {
4475 jQuery.ajax({
4476 type: 'POST', url: ajaxurl,
4477 data: {
4478 'action': 'frm_settings_tab',
4479 'tab': holder.replace( '_settings', '' ),
4480 'nonce': frmGlobal.nonce
4481 },
4482 success: function( html ) {
4483 holderContainer.replaceWith( html );
4484 }
4485 });
4486 }
4487 }
4488
4489 function uninstallNow() {
4490 /*jshint validthis:true */
4491 if ( confirmLinkClick( this ) === true ) {
4492 jQuery( '.frm_uninstall .frm-wait' ).css( 'visibility', 'visible' );
4493 jQuery.ajax({
4494 type: 'POST',
4495 url: ajaxurl,
4496 data: 'action=frm_uninstall&nonce=' + frmGlobal.nonce,
4497 success: function( msg ) {
4498 jQuery( '.frm_uninstall' ).fadeOut( 'slow' );
4499 window.location = msg;
4500 }
4501 });
4502 }
4503 return false;
4504 }
4505
4506 function saveAddonLicense() {
4507 /*jshint validthis:true */
4508 var button = jQuery( this );
4509 var buttonName = this.name;
4510 var pluginSlug = this.getAttribute( 'data-plugin' );
4511 var action = buttonName.replace( 'edd_' + pluginSlug + '_license_', '' );
4512 var license = document.getElementById( 'edd_' + pluginSlug + '_license_key' ).value;
4513 jQuery.ajax({
4514 type: 'POST', url: ajaxurl, dataType: 'json',
4515 data: {action: 'frm_addon_' + action, license: license, plugin: pluginSlug, nonce: frmGlobal.nonce},
4516 success: function( msg ) {
4517 var thisRow = button.closest( '.edd_frm_license_row' );
4518 if ( action === 'deactivate' ) {
4519 license = '';
4520 document.getElementById( 'edd_' + pluginSlug + '_license_key' ).value = '';
4521 }
4522 thisRow.find( '.edd_frm_license' ).html( license );
4523 if ( msg.success === true ) {
4524 thisRow.find( '.frm_icon_font' ).removeClass( 'frm_hidden' );
4525 thisRow.find( 'div.alignleft' ).toggleClass( 'frm_hidden', 1000 );
4526 }
4527
4528 var messageBox = thisRow.find( '.frm_license_msg' );
4529 messageBox.html( msg.message );
4530 if ( msg.message !== '' ) {
4531 setTimeout( function() {
4532 messageBox.html( '' );
4533 }, 15000 );
4534 }
4535 }
4536 });
4537 }
4538
4539 /* Import/Export page */
4540
4541 function startFormMigration( event ) {
4542 event.preventDefault();
4543
4544 var checkedBoxes = jQuery( event.target ).find( 'input:checked' );
4545 if ( ! checkedBoxes.length ) {
4546 return;
4547 }
4548
4549 var ids = [];
4550 checkedBoxes.each( function( i ) {
4551 ids[i] = this.value;
4552 });
4553
4554 // Begin the import process.
4555 importForms( ids, event.target );
4556 }
4557
4558 /**
4559 * Begins the process of importing the forms.
4560 */
4561 function importForms( forms, targetForm ) {
4562
4563 // Hide the form select section.
4564 var $form = jQuery( targetForm ),
4565 $processSettings = $form.next( '.frm-importer-process' );
4566
4567 // Display total number of forms we have to import.
4568 $processSettings.find( '.form-total' ).text( forms.length );
4569 $processSettings.find( '.form-current' ).text( '1' );
4570
4571 $form.hide();
4572
4573 // Show processing status.
4574 // '.process-completed' might have been shown earlier during a previous import, so hide now.
4575 $processSettings.find( '.process-completed' ).hide();
4576 $processSettings.show();
4577
4578 // Create global import queue.
4579 s.importQueue = forms;
4580 s.imported = 0;
4581
4582 // Import the first form in the queue.
4583 importForm( $processSettings );
4584 }
4585
4586 /**
4587 * Imports a single form from the import queue.
4588 */
4589 function importForm( $processSettings ) {
4590 var formID = s.importQueue[0],
4591 provider = $processSettings.closest( '.welcome-panel-content' ).find( 'input[name="slug"]' ).val(),
4592 data = {
4593 action: 'frm_import_' + provider,
4594 form_id: formID,
4595 nonce: frmGlobal.nonce
4596 };
4597
4598 // Trigger AJAX import for this form.
4599 jQuery.post( ajaxurl, data, function( res ) {
4600
4601 if ( res.success ) {
4602 var statusUpdate;
4603
4604 if ( res.data.error ) {
4605 statusUpdate = '<p>' + res.data.name + ': ' + res.data.msg + '</p>';
4606 } else {
4607 statusUpdate = '<p>Imported <a href="' + res.data.link + '" target="_blank">' + res.data.name + '</a></p>';
4608 }
4609
4610 $processSettings.find( '.status' ).prepend( statusUpdate );
4611 $processSettings.find( '.status' ).show();
4612
4613 // Remove this form ID from the queue.
4614 s.importQueue = jQuery.grep( s.importQueue, function( value ) {
4615 return value != formID;
4616 });
4617 s.imported++;
4618
4619 if ( s.importQueue.length === 0 ) {
4620 $processSettings.find( '.process-count' ).hide();
4621 $processSettings.find( '.forms-completed' ).text( s.imported );
4622 $processSettings.find( '.process-completed' ).show();
4623 } else {
4624 // Import next form in the queue.
4625 $processSettings.find( '.form-current' ).text( s.imported + 1 );
4626 importForm( $processSettings );
4627 }
4628 }
4629 });
4630 }
4631
4632 function validateExport( e ) {
4633 /*jshint validthis:true */
4634 e.preventDefault();
4635
4636 var s = false;
4637 var $exportForms = jQuery( 'input[name="frm_export_forms[]"]' );
4638
4639 if ( ! jQuery( 'input[name="frm_export_forms[]"]:checked' ).val() ) {
4640 $exportForms.closest( '.frm-table-box' ).addClass( 'frm_blank_field' );
4641 s = 'stop';
4642 }
4643
4644 var $exportType = jQuery( 'input[name="type[]"]' );
4645 if ( ! jQuery( 'input[name="type[]"]:checked' ).val() && $exportType.attr( 'type' ) === 'checkbox' ) {
4646 $exportType.closest( 'p' ).addClass( 'frm_blank_field' );
4647 s = 'stop';
4648 }
4649
4650 if ( s === 'stop' ) {
4651 return false;
4652 }
4653
4654 e.stopPropagation();
4655 this.submit();
4656 }
4657
4658 function removeExportError() {
4659 /*jshint validthis:true */
4660 var t = jQuery( this ).closest( '.frm_blank_field' );
4661 if ( typeof t === 'undefined' ) {
4662 return;
4663 }
4664
4665 var $thisName = this.name;
4666 if ( $thisName === 'type[]' && jQuery( 'input[name="type[]"]:checked' ).val() ) {
4667 t.removeClass( 'frm_blank_field' );
4668 } else if ( $thisName === 'frm_export_forms[]' && jQuery( this ).val() ) {
4669 t.removeClass( 'frm_blank_field' );
4670 }
4671
4672 }
4673
4674 function checkCSVExtension() {
4675 /*jshint validthis:true */
4676 var f = jQuery( this ).val();
4677 var re = /\.csv$/i;
4678 if ( f.match( re ) !== null ) {
4679 jQuery( '.show_csv' ).fadeIn();
4680 } else {
4681 jQuery( '.show_csv' ).fadeOut();
4682 }
4683 }
4684
4685 function checkExportTypes() {
4686 /*jshint validthis:true */
4687 var $dropdown = jQuery( this );
4688 var $selected = $dropdown.find( ':selected' );
4689 var s = $selected.data( 'support' );
4690
4691 var multiple = s.indexOf( '|' );
4692 jQuery( 'input[name="type[]"]' ).each( function() {
4693 this.checked = false;
4694 if ( s.indexOf( this.value ) >= 0 ) {
4695 this.disabled = false;
4696 if ( multiple === -1 ) {
4697 this.checked = true;
4698 }
4699 } else {
4700 this.disabled = true;
4701 }
4702 });
4703
4704 if ( $dropdown.val() === 'csv' ) {
4705 jQuery( '.csv_opts' ).show();
4706 jQuery( '.xml_opts' ).hide();
4707 } else {
4708 jQuery( '.csv_opts' ).hide();
4709 jQuery( '.xml_opts' ).show();
4710 }
4711
4712 var c = $selected.data( 'count' );
4713 var exportField = jQuery( 'input[name="frm_export_forms[]"]' );
4714 if ( c === 'single' ) {
4715 exportField.prop( 'multiple', false );
4716 exportField.removeAttr( 'checked' );
4717 } else {
4718 exportField.prop( 'multiple', true );
4719 exportField.removeAttr( 'disabled' );
4720 }
4721 }
4722
4723 function preventMultipleExport() {
4724 var type = jQuery( 'select[name=format]' ),
4725 selected = type.find( ':selected' ),
4726 count = selected.data( 'count' ),
4727 exportField = jQuery( 'input[name="frm_export_forms[]"]' );
4728
4729 if ( count === 'single' ) {
4730 // Disable all other fields to prevent multiple selections.
4731 if ( this.checked ) {
4732 exportField.attr( 'disabled', true );
4733 this.removeAttribute( 'disabled' );
4734 } else {
4735 exportField.removeAttr( 'disabled' );
4736 }
4737 } else {
4738 exportField.removeAttr( 'disabled' );
4739 }
4740 }
4741
4742 function initiateMultiselect() {
4743 jQuery( '.frm_multiselect' ).multiselect({
4744 templates: {ul: '<ul class="multiselect-container frm-dropdown-menu"></ul>'},
4745 buttonContainer: '<div class="btn-group frm-btn-group dropdown" />',
4746 nonSelectedText: frm_admin_js.default_label,
4747 onDropdownShown: function( event ) {
4748 var action = jQuery( event.currentTarget.closest( '.frm_form_action_settings, #frm-show-fields' ) );
4749 if ( action.length ) {
4750 jQuery( '#wpcontent' ).click( function() {
4751 if ( jQuery( '.multiselect-container.frm-dropdown-menu' ).is( ':visible' ) ) {
4752 jQuery( event.currentTarget ).removeClass( 'open' );
4753 }
4754 });
4755 }
4756 }
4757 });
4758 }
4759
4760 /* Addons page */
4761 function activateAddon( e ) {
4762 e.preventDefault();
4763 installOrActivate( this, 'frm_activate_addon' );
4764 }
4765
4766 function installAddon( e ) {
4767 e.preventDefault();
4768 installOrActivate( this, 'frm_install_addon' );
4769 }
4770
4771 function installOrActivate( clicked, action ) {
4772 // Remove any leftover error messages, output an icon and get the plugin basename that needs to be activated.
4773 jQuery( '.frm-addon-error' ).remove();
4774 var button = jQuery( clicked );
4775 var plugin = button.attr( 'rel' );
4776 var el = button.parent();
4777 var message = el.parent().find( '.addon-status-label' );
4778
4779 button.addClass( 'frm_loading_button' );
4780
4781 // Process the Ajax to perform the activation.
4782 jQuery.ajax({
4783 url: ajaxurl,
4784 type: 'POST',
4785 async: true,
4786 cache: false,
4787 dataType: 'json',
4788 data: {
4789 action: action,
4790 nonce: frmGlobal.nonce,
4791 plugin: plugin
4792 },
4793 success: function( response ) {
4794 // If there is a WP Error instance, output it here and quit the script.
4795 if ( response.error ) {
4796 addonError( response, el, button );
4797 return;
4798 }
4799
4800 // If we need more credentials, output the form sent back to us.
4801 if ( response.form ) {
4802 // Display the form to gather the users credentials.
4803
4804 button.append( '<div class="frm-addon-error frm_error_style">' + response.form + '</div>' );
4805 loader.hide();
4806
4807 // Add a disabled attribute the install button if the creds are needed.
4808 button.attr( 'disabled', true );
4809
4810 el.on( 'click', '#upgrade', 'installAddonWithCreds' );
4811
4812 // No need to move further if we need to enter our creds.
4813 return;
4814 }
4815
4816 afterAddonInstall( response, button, message, el );
4817 },
4818 error: function() {
4819 button.removeClass( 'frm_loading_button' );
4820 }
4821 });
4822 }
4823
4824 function installAddonWithCreds( e ) {
4825 // Prevent the default action, let the user know we are attempting to install again and go with it.
4826 e.preventDefault();
4827
4828 // Now let's make another Ajax request once the user has submitted their credentials.
4829 var proceed = jQuery( this );
4830 var el = proceed.parent().parent();
4831
4832 proceed.addClass( 'frm_loading_button' );
4833
4834 jQuery.ajax({
4835 url: ajaxurl,
4836 type: 'POST',
4837 async: true,
4838 cache: false,
4839 dataType: 'json',
4840 data: {
4841 action: 'frm_install_addon',
4842 nonce: frm_admin_js.nonce,
4843 plugin: plugin,
4844 hostname: el.find( '#hostname' ).val(),
4845 username: el.find( '#username' ).val(),
4846 password: el.find( '#password' ).val()
4847 },
4848 success: function( response ) {
4849 // If there is a WP Error instance, output it here and quit the script.
4850 if ( response.error ) {
4851 addonError( response, el, button );
4852 return;
4853 }
4854
4855 if ( response.form ) {
4856 loader.hide();
4857 jQuery( '.frm-inline-error' ).remove();
4858 //proceed.val(admin.proceed);
4859 //proceed.after('<span class="frm-inline-error">' + admin.connect_error + '</span>' );
4860 return;
4861 }
4862
4863 afterAddonInstall( response, proceed, message, el );
4864 },
4865 error: function() {
4866 proceed.removeClass( 'frm_loading_button' );
4867 }
4868 });
4869 }
4870
4871 function afterAddonInstall( response, button, message, el ) {
4872 // The Ajax request was successful, so let's update the output.
4873 button.css({ 'opacity': '0' });
4874 message.text( frm_admin_js.active );
4875 jQuery( '#frm-oneclick' ).hide();
4876 jQuery( '#frm-addon-status' ).text( response ).show();
4877 jQuery( '#frm_upgrade_modal h2' ).hide();
4878 jQuery( '#frm_upgrade_modal .frm_lock_icon' ).addClass( 'frm_lock_open_icon' );
4879 jQuery( '#frm_upgrade_modal .frm_lock_icon use' ).attr( 'xlink:href', '#frm_lock_open_icon' );
4880
4881 // Proceed with CSS changes
4882 el.parent().removeClass( 'frm-addon-not-installed frm-addon-installed' ).addClass( 'frm-addon-active' );
4883 button.removeClass( 'frm_loading_button' );
4884
4885 // Maybe refresh import and SMTP pages
4886 var refreshPage = document.querySelectorAll( '.frm-admin-page-import, #frm-admin-smtp' );
4887 if ( refreshPage.length > 0 ) {
4888 window.location.reload();
4889 }
4890 }
4891
4892 function addonError( response, el, button ) {
4893 el.append( '<div class="frm-addon-error frm_error_style"><p><strong>' + response.error + '</strong></p></div>' );
4894 button.removeClass( 'frm_loading_button' );
4895 jQuery( '.frm-addon-error' ).delay( 4000 ).fadeOut();
4896 }
4897
4898 /* Templates */
4899
4900 function initNewFormModal() {
4901 var $info = initModal( '#frm_form_modal', '650px' );
4902 if ( $info === false ) {
4903 return;
4904 }
4905
4906 jQuery( '.frm-new-form-button' ).click( function( event ) {
4907 event.preventDefault();
4908 $info.dialog( 'open' );
4909 });
4910
4911 jQuery( document ).on( 'submit', '#frm-new-form', installTemplate );
4912 }
4913
4914 function initTemplateModal() {
4915 var $preview = initModal( '#frm_preview_template_modal', '700px' );
4916 if ( $preview !== false ) {
4917 jQuery( '.frm-preview-template' ).click( function( event ) {
4918 event.preventDefault();
4919 var link = this.attributes.rel.value,
4920 cont = document.getElementById( 'frm-preview-block' );
4921
4922 if ( link.indexOf( ajaxurl ) > -1 ) {
4923 var iframe = document.createElement( 'iframe' );
4924 iframe.src = link;
4925 iframe.height = '400';
4926 iframe.width = '100%';
4927 cont.innerHTML = '';
4928 cont.appendChild( iframe );
4929 } else {
4930 frmApiPreview( cont, link );
4931 }
4932 $preview.dialog( 'open' );
4933 });
4934 }
4935
4936 var $info = initModal( '#frm_template_modal', '650px' );
4937 if ( $info === false ) {
4938 return;
4939 }
4940
4941 jQuery( '.frm-install-template' ).click( function( event ) {
4942 event.preventDefault();
4943 var oldName = jQuery( this ).closest( 'li, td' ).find( 'h3' ).html(),
4944 nameLabel = document.getElementById( 'frm_new_name' ),
4945 descLabel = document.getElementById( 'frm_new_desc' );
4946
4947 document.getElementById( 'frm_template_name' ).value = oldName;
4948 document.getElementById( 'frm_link' ).value = this.attributes.rel.value;
4949 document.getElementById( 'frm_action_type' ).value = 'frm_install_template';
4950 nameLabel.innerHTML = nameLabel.getAttribute( 'data-form' );
4951 descLabel.innerHTML = descLabel.getAttribute( 'data-form' );
4952 $info.dialog( 'open' );
4953 });
4954
4955 jQuery( '.frm-build-template' ).click( function( event ) {
4956 event.preventDefault();
4957 var nameLabel = document.getElementById( 'frm_new_name' ),
4958 descLabel = document.getElementById( 'frm_new_desc' );
4959
4960 nameLabel.innerHTML = nameLabel.getAttribute( 'data-template' );
4961 descLabel.innerHTML = descLabel.getAttribute( 'data-template' );
4962 document.getElementById( 'frm_template_name' ).value = this.getAttribute( 'data-fullname' );
4963 document.getElementById( 'frm_link' ).value = this.getAttribute( 'data-formid' );
4964 document.getElementById( 'frm_action_type' ).value = 'frm_build_template';
4965 $info.dialog( 'open' );
4966 });
4967
4968 jQuery( '.frm-new-form-button' ).click( function( event ) {
4969 event.preventDefault();
4970 var nameLabel = document.getElementById( 'frm_new_name' ),
4971 descLabel = document.getElementById( 'frm_new_desc' );
4972
4973 nameLabel.innerHTML = nameLabel.getAttribute( 'data-form' );
4974 descLabel.innerHTML = descLabel.getAttribute( 'data-form' );
4975 document.getElementById( 'frm_template_name' ).value = '';
4976 document.getElementById( 'frm_link' ).value = '';
4977 document.getElementById( 'frm_action_type' ).value = 'frm_install_form';
4978 $info.dialog( 'open' );
4979 });
4980
4981 jQuery( document ).on( 'submit', '#frm-new-template', installTemplate );
4982 }
4983
4984 function initSelectionAutocomplete() {
4985 if ( jQuery.fn.autocomplete ) {
4986 initAutocomplete( 'page' );
4987 initAutocomplete( 'user' );
4988 }
4989 }
4990
4991 function initAutocomplete( type ) {
4992 if ( jQuery( '.frm-' + type + '-search' ).length < 1 ) {
4993 return;
4994 }
4995
4996 jQuery( '.frm-' + type + '-search' ).autocomplete({
4997 delay: 100,
4998 minLength: 0,
4999 source: ajaxurl + '?action=frm_' + type + '_search&nonce=' + frmGlobal.nonce,
5000 select: autoCompleteSelectFromResults,
5001 focus: autoCompleteFocus,
5002 position: {
5003 my: 'left top',
5004 at: 'left bottom',
5005 collision: 'flip'
5006 },
5007 response: function( event, ui ) {
5008 if ( ! ui.content.length ) {
5009 var noResult = { value: '', label: frm_admin_js.no_items_found };
5010 ui.content.push( noResult );
5011 }
5012 },
5013 create: function() {
5014 var $container = jQuery( this ).parent();
5015
5016 if ( $container.length === 0 ) {
5017 $container = 'body';
5018 }
5019
5020 jQuery( this ).autocomplete( 'option', 'appendTo', $container );
5021 }
5022 })
5023 .focus( function() {
5024 // Show options on click to make it work more like a dropdown.
5025 if ( this.value === '' || this.nextElementSibling.value < 1 ) {
5026 jQuery( this ).autocomplete( 'search', this.value );
5027 }
5028 });
5029 }
5030
5031 /**
5032 * Prevent the value from changing when using keyboard to scroll.
5033 */
5034 function autoCompleteFocus() {
5035 return false;
5036 }
5037
5038 function autoCompleteSelectFromResults( e, ui ) {
5039 e.preventDefault();
5040
5041 if ( ui.item.value === '' ) {
5042 this.value = '';
5043 } else {
5044 this.value = ui.item.label;
5045 }
5046
5047 this.nextElementSibling.value = ui.item.value;
5048 }
5049
5050 function frmApiPreview( cont, link ) {
5051 cont.innerHTML = '<div class="frm-wait"></div>';
5052 jQuery.ajax({
5053 dataType: 'json',
5054 url: link,
5055 success: function( json ) {
5056 var form = json.renderedHtml;
5057 form = form.replace( /<script\b[^<]*(js\/jquery\/jquery)[^<]*><\/script>/gi, '' );
5058 form = form.replace( /<link\b[^>]*(jquery-ui.min.css)[^>]*>/gi, '' );
5059 form = form.replace( ' frm_logic_form ', ' ' );
5060 form = form.replace( '<form ', '<form onsubmit="event.preventDefault();" ' );
5061 cont.innerHTML = '<div class="frm-wait" id="frm-remove-me"></div><div class="frm-fade" id="frm-show-me">' +
5062 form + '</div>';
5063 setTimeout( function() {
5064 document.getElementById( 'frm-remove-me' ).style.display = 'none';
5065 document.getElementById( 'frm-show-me' ).style.opacity = '1';
5066 }, 300 );
5067 }
5068 });
5069 }
5070
5071 function installTemplate( e ) {
5072 /*jshint validthis:true */
5073 var action = this.elements.type.value,
5074 button = this.querySelector( 'button' );
5075 e.preventDefault();
5076 button.classList.add( 'frm_loading_button' );
5077 installNewForm( this, action );
5078 }
5079
5080 function installNewForm( form, action ) {
5081 var data,
5082 formName = form.elements.template_name.value,
5083 formDesc = form.elements.template_desc.value,
5084 link = form.elements.link.value;
5085
5086 data = {
5087 action: action,
5088 xml: link,
5089 name: formName,
5090 desc: formDesc,
5091 nonce: frmGlobal.nonce
5092 };
5093 postAjax( data, function( response ) {
5094 if ( typeof response.redirect !== 'undefined' ) {
5095 window.location = response.redirect;
5096 } else {
5097 jQuery( '.spinner' ).css( 'visibility', 'hidden' );
5098 // TODO: show response.message
5099 }
5100 });
5101 }
5102
5103 function trashTemplate( e ) {
5104 /*jshint validthis:true */
5105 var id = this.getAttribute( 'data-id' );
5106 e.preventDefault();
5107
5108 data = {
5109 action: 'frm_forms_trash',
5110 id: id,
5111 nonce: frmGlobal.nonce
5112 };
5113 postAjax( data, function() {
5114 var card = document.getElementById( 'frm-template-custom-' + id );
5115 fadeOut( card, function() {
5116 card.parentNode.removeChild( card );
5117 });
5118 });
5119 }
5120
5121 function searchContent() {
5122 /*jshint validthis:true */
5123 var i,
5124 regEx = false,
5125 searchText = this.value.toLowerCase(),
5126 toSearch = this.getAttribute( 'data-tosearch' ),
5127 items = document.getElementsByClassName( toSearch );
5128
5129 if ( this.tagName === 'SELECT' ) {
5130 searchText = selectedOptions( this );
5131 searchText = searchText.join( '|' ).toLowerCase();
5132 regEx = true;
5133 }
5134
5135 if ( toSearch === 'frm-action' && searchText !== '' ) {
5136 var addons = document.getElementById( 'frm_email_addon_menu' ).classList;
5137 addons.remove( 'frm-all-actions' );
5138 addons.add( 'frm-limited-actions' );
5139 }
5140
5141 for ( i = 0; i < items.length; i++ ) {
5142 var innerText = items[i].innerText.toLowerCase();
5143 if ( searchText === '' ) {
5144 items[i].classList.remove( 'frm_hidden' );
5145 items[i].classList.remove( 'frm-search-result' );
5146 } else if ( ( regEx && new RegExp( searchText ).test( innerText ) ) || innerText.indexOf( searchText ) >= 0 ) {
5147 items[i].classList.remove( 'frm_hidden' );
5148 items[i].classList.add( 'frm-search-result' );
5149 } else {
5150 items[i].classList.add( 'frm_hidden' );
5151 items[i].classList.remove( 'frm-search-result' );
5152 }
5153 }
5154 }
5155
5156 function stopPropagation( e ) {
5157 e.stopPropagation();
5158 }
5159
5160 /* Helpers */
5161
5162 function selectedOptions( select ) {
5163 var opt,
5164 result = [],
5165 options = select && select.options;
5166
5167 for ( var i = 0, iLen = options.length; i < iLen; i++ ) {
5168 opt = options[i];
5169
5170 if ( opt.selected ) {
5171 result.push( opt.value );
5172 }
5173 }
5174 return result;
5175 }
5176
5177 function triggerEvent( element, event ) {
5178 var evt = document.createEvent( 'HTMLEvents' );
5179 evt.initEvent( event, false, true );
5180 element.dispatchEvent( evt );
5181 }
5182
5183 function postAjax( data, success ) {
5184 var xmlHttp = new XMLHttpRequest();
5185 var params = typeof data === 'string' ? data : Object.keys( data ).map(
5186 function( k ) {
5187 return encodeURIComponent( k ) + '=' + encodeURIComponent( data[k]);
5188 }
5189 ).join( '&' );
5190
5191 xmlHttp.open( 'post', ajaxurl, true );
5192 xmlHttp.onreadystatechange = function() {
5193 if ( xmlHttp.readyState > 3 && xmlHttp.status == 200 ) {
5194 var response = xmlHttp.responseText;
5195 if ( response !== '' ) {
5196 response = JSON.parse( response );
5197 }
5198 success( response );
5199 }
5200 };
5201 xmlHttp.setRequestHeader( 'X-Requested-With', 'XMLHttpRequest' );
5202 xmlHttp.setRequestHeader( 'Content-type', 'application/x-www-form-urlencoded' );
5203 xmlHttp.send( params );
5204 return xmlHttp;
5205 }
5206
5207 function fadeOut( element, success ) {
5208 element.classList.add( 'frm-fade' );
5209 setTimeout( success, 1000 );
5210 }
5211
5212 function invisible( classes ) {
5213 jQuery( classes ).css( 'visibility', 'hidden' );
5214 }
5215
5216 function visible( classes ) {
5217 jQuery( classes ).css( 'visibility', 'visible' );
5218 }
5219
5220 function initModal( id, width ) {
5221 var $info = jQuery( id );
5222 if ( $info.length < 1 ) {
5223 return false;
5224 }
5225
5226 if ( typeof width === 'undefined' ) {
5227 width = '550px';
5228 }
5229 $info.dialog({
5230 dialogClass: 'frm-dialog',
5231 modal: true,
5232 autoOpen: false,
5233 closeOnEscape: true,
5234 width: width,
5235 resizable: false,
5236 draggable: false,
5237 open: function() {
5238 jQuery( '.ui-dialog-titlebar' ).addClass( 'frm_hidden' ).removeClass( 'ui-helper-clearfix' );
5239 jQuery( '#wpwrap' ).addClass( 'frm_overlay' );
5240 jQuery( '.frm-dialog' ).removeClass( 'ui-widget ui-widget-content ui-corner-all' );
5241 jQuery( id ).removeClass( 'ui-dialog-content ui-widget-content' );
5242
5243 // close dialog by clicking the overlay behind it
5244 jQuery( '.ui-widget-overlay, a.dismiss' ).bind( 'click', function() {
5245 $info.dialog( 'close' );
5246 });
5247 },
5248 close: function() {
5249 jQuery( '#wpwrap' ).removeClass( 'frm_overlay' );
5250 jQuery( '.spinner' ).css( 'visibility', 'hidden' );
5251 }
5252 });
5253
5254 return $info;
5255 }
5256
5257 function toggle( cname, id ) {
5258 if ( id === '#' ) {
5259 var cont = document.getElementById( cname );
5260 var hidden = cont.style.display;
5261 if ( hidden === 'none' ) {
5262 cont.style.display = 'block';
5263 } else {
5264 cont.style.display = 'none';
5265 }
5266 } else {
5267 var vis = cname.is( ':visible' );
5268 if ( vis ) {
5269 cname.hide();
5270 } else {
5271 cname.show();
5272 }
5273 }
5274 }
5275
5276 function removeWPUnload() {
5277 window.onbeforeunload = null;
5278 var w = jQuery( window );
5279 w.off( 'beforeunload.widgets' );
5280 w.off( 'beforeunload.edit-post' );
5281 }
5282
5283 function maybeChangeEmbedFormMsg() {
5284 var fieldId = jQuery( this ).closest( '.frm-single-settings' ).data( 'fid' );
5285 var fieldItem = document.getElementById( 'frm_field_id_' + fieldId );
5286 if ( null === fieldItem || 'form' !== fieldItem.dataset.type ) {
5287 return;
5288 }
5289
5290 fieldItem = jQuery( fieldItem );
5291
5292 if ( this.options[ this.selectedIndex ].value ) {
5293 fieldItem.find( '.frm-not-set' )[0].classList.add( 'frm_hidden' );
5294 var embedMsg = fieldItem.find( '.frm-embed-message' );
5295 embedMsg.html( embedMsg.data( 'embedmsg' ) + this.options[ this.selectedIndex ].text );
5296 fieldItem.find( '.frm-embed-field-placeholder' )[0].classList.remove( 'frm_hidden' );
5297 } else {
5298 fieldItem.find( '.frm-not-set' )[0].classList.remove( 'frm_hidden' );
5299 fieldItem.find( '.frm-embed-field-placeholder' )[0].classList.add( 'frm_hidden' );
5300 }
5301 }
5302
5303 function toggleProductType() {
5304 var settings = jQuery( this ).closest( '.frm-single-settings' ),
5305 container = settings.find( '.frmjs_product_choices' ),
5306 heading = settings.find( '.frm_prod_options_heading' ),
5307 currentVal = this.options[ this.selectedIndex ].value;
5308
5309 container.removeClass( 'frm_prod_type_single frm_prod_type_user_def' );
5310 heading.removeClass( 'frm_prod_user_def' );
5311
5312 if ( 'single' === currentVal ) {
5313 container.addClass( 'frm_prod_type_single' );
5314 } else if ( 'user_def' === currentVal ) {
5315 container.addClass( 'frm_prod_type_user_def' );
5316 heading.addClass( 'frm_prod_user_def' );
5317 }
5318 }
5319
5320 function isProductField( fieldId ) {
5321 var field = document.getElementById( 'frm_field_id_' + fieldId );
5322 if ( field === null ) {
5323 return false;
5324 } else {
5325 return 'product' === field.getAttribute( 'data-type' );
5326 }
5327 }
5328
5329 return {
5330 init: function() {
5331 s = {};
5332
5333 // Bootstrap dropdown button
5334 jQuery( '.wp-admin' ).click( function( e ) {
5335 var t = jQuery( e.target );
5336 var $openDrop = jQuery( '.dropdown.open' );
5337 if ( $openDrop.length && ! t.hasClass( 'dropdown' ) && ! t.closest( '.dropdown' ).length ) {
5338 $openDrop.removeClass( 'open' );
5339 }
5340 });
5341 jQuery( '#frm_bs_dropdown:not(.open) a' ).click( focusSearchBox );
5342
5343 if ( typeof thisFormId === 'undefined' ) {
5344 thisFormId = jQuery( document.getElementById( 'form_id' ) ).val();
5345 }
5346
5347 if ( $newFields.length > 0 ) {
5348 // only load this on the form builder page
5349 frmAdminBuild.buildInit();
5350 } else if ( document.getElementById( 'frm_notification_settings' ) !== null ) {
5351 // only load on form settings page
5352 frmAdminBuild.settingsInit();
5353 } else if ( document.getElementById( 'frm_styling_form' ) !== null ) {
5354 // load styling settings js
5355 frmAdminBuild.styleInit();
5356 } else if ( document.getElementById( 'frm_custom_css_box' ) !== null ) {
5357 // load styling settings js
5358 frmAdminBuild.customCSSInit();
5359 } else if ( document.getElementById( 'form_global_settings' ) !== null ) {
5360 // global settings page
5361 frmAdminBuild.globalSettingsInit();
5362 } else if ( document.getElementById( 'frm_export_xml' ) !== null ) {
5363 // import/export page
5364 frmAdminBuild.exportInit();
5365 } else if ( document.getElementById( 'frm-templates-page' ) !== null ) {
5366 frmAdminBuild.templateInit();
5367 } else if ( document.getElementById( 'frm_dyncontent' ) !== null ) {
5368 // only load on views settings page
5369 frmAdminBuild.viewInit();
5370 } else if ( document.getElementById( 'frm_inbox_page' ) !== null ) {
5371 // Inbox page
5372 frmAdminBuild.inboxInit();
5373 } else {
5374 // New form selection page
5375 initNewFormModal();
5376 initSelectionAutocomplete();
5377
5378 jQuery( '[data-frmprint]' ).click( function() {
5379 window.print();
5380 return false;
5381 });
5382 }
5383
5384 var $advInfo = jQuery( document.getElementById( 'frm_adv_info' ) );
5385 if ( $advInfo.length > 0 || jQuery( '.frm_field_list' ).length > 0 ) {
5386 // only load on the form, form settings, and view settings pages
5387 frmAdminBuild.panelInit();
5388 }
5389
5390 loadTooltips();
5391 initUpgradeModal();
5392
5393 // used on build, form settings, and view settings
5394 var $shortCodeDiv = jQuery( document.getElementById( 'frm_shortcodediv' ) );
5395 if ( $shortCodeDiv.length > 0 ) {
5396 jQuery( 'a.edit-frm_shortcode' ).click( function() {
5397 if ( $shortCodeDiv.is( ':hidden' ) ) {
5398 $shortCodeDiv.slideDown( 'fast' );
5399 this.style.display = 'none';
5400 }
5401 return false;
5402 });
5403
5404 jQuery( '.cancel-frm_shortcode', '#frm_shortcodediv' ).click( function() {
5405 $shortCodeDiv.slideUp( 'fast' );
5406 $shortCodeDiv.siblings( 'a.edit-frm_shortcode' ).show();
5407 return false;
5408 });
5409 }
5410
5411 // tabs
5412 jQuery( document ).on( 'click', '#frm-nav-tabs a', clickNewTab );
5413 jQuery( '.post-type-frm_display .frm-nav-tabs a, .frm-category-tabs a, #frm-templates-page .frm-nav-tabs a' ).click( function() {
5414 if ( ! this.classList.contains( 'frm_noallow' ) ) {
5415 clickTab( this );
5416 return false;
5417 }
5418 });
5419 clickTab( jQuery( '.starttab a' ), 'auto' );
5420
5421 // submit the search form with dropdown
5422 jQuery( '#frm-fid-search-menu a' ).click( function() {
5423 var val = this.id.replace( 'fid-', '' );
5424 jQuery( 'select[name="fid"]' ).val( val );
5425 jQuery( document.getElementById( 'posts-filter' ) ).submit();
5426 return false;
5427 });
5428
5429 jQuery( '.frm_select_box' ).on( 'click focus', function() {
5430 this.select();
5431 });
5432
5433 jQuery( document ).on( 'input search change', '.frm-auto-search', searchContent );
5434 jQuery( document ).on( 'focusin click', '.frm-auto-search', stopPropagation );
5435 var autoSearch = jQuery( '.frm-auto-search' );
5436 if ( autoSearch.val() !== '' ) {
5437 autoSearch.keyup();
5438 }
5439
5440 // Initialize Formidable Connection.
5441 FrmFormsConnect.init();
5442
5443 jQuery( document ).on( 'click', '.frm-install-addon', installAddon );
5444 jQuery( document ).on( 'click', '.frm-activate-addon', activateAddon );
5445
5446 // prevent annoying confirmation message from WordPress
5447 jQuery( 'button, input[type=submit]' ).on( 'click', removeWPUnload );
5448 },
5449
5450 buildInit: function() {
5451 if ( jQuery( '.frm_field_loading' ).length ) {
5452 var loadFieldId = jQuery( '.frm_field_loading' ).first().attr( 'id' );
5453 loadFields( loadFieldId );
5454 }
5455
5456 setupSortable( 'ul.frm_sorting' );
5457
5458 // Show message if section has no fields inside
5459 var frmSorting = jQuery( '.start_divider.frm_sorting' );
5460 for ( i = 0; i < frmSorting.length; i++ ) {
5461 if ( frmSorting[i].children.length < 2 ) {
5462 jQuery( frmSorting[i]).parent().children( '.frm_no_section_fields' ).addClass( 'frm_block' );
5463 }
5464 }
5465
5466 jQuery( '.field_type_list > li:not(.frm_noallow)' ).draggable({
5467 connectToSortable: '#frm-show-fields',
5468 helper: 'clone',
5469 revert: 'invalid',
5470 delay: 10,
5471 cancel: '.frm-dropdown-menu'
5472 });
5473 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();
5474
5475 jQuery( '.frm_submit_ajax' ).click( submitBuild );
5476 jQuery( '.frm_submit_no_ajax' ).click( submitNoAjax );
5477
5478 jQuery( 'a.edit-form-status' ).click( slideDown );
5479 jQuery( '.cancel-form-status' ).click( slideUp );
5480 jQuery( '.save-form-status' ).click( function() {
5481 var newStatus = jQuery( document.getElementById( 'form_change_status' ) ).val();
5482 jQuery( 'input[name="new_status"]' ).val( newStatus );
5483 jQuery( document.getElementById( 'form-status-display' ) ).html( newStatus );
5484 jQuery( '.cancel-form-status' ).click();
5485 return false;
5486 });
5487
5488 jQuery( '.frm_form_builder form:first' ).submit( function() {
5489 jQuery( '.inplace_field' ).blur();
5490 });
5491
5492 initiateMultiselect();
5493 renumberPageBreaks();
5494
5495 var $builderForm = jQuery( builderForm );
5496 var builderArea = document.getElementById( 'frm_form_editor_container' );
5497 $builderForm.on( 'click', '.frm_add_logic_row', addFieldLogicRow );
5498 $builderForm.on( 'click', '.frm_add_watch_lookup_row', addWatchLookupRow );
5499 $builderForm.on( 'change', '.frm_get_values_form', updateGetValueFieldSelection );
5500 $builderForm.on( 'change', '.frm_logic_field_opts', getFieldValues );
5501 $builderForm.on( 'change', '.scale_maxnum, .scale_minnum', setScaleValues );
5502 $builderForm.on( 'change', '.radio_maxnum', setStarValues );
5503
5504 jQuery( document.getElementById( 'frm-insert-fields' ) ).on( 'click', '.frm_add_field', addFieldClick );
5505 $newFields.on( 'click', '.frm_clone_field', duplicateField );
5506 $builderForm.on( 'blur', 'input[id^="frm_calc"]', checkCalculationCreatedByUser );
5507 $builderForm.on( 'change', 'input.frm_format_opt', toggleInvalidMsg );
5508 $builderForm.on( 'change click', '[data-changeme]', liveChanges );
5509 $builderForm.on( 'click', 'input.frm_req_field', markRequired );
5510 $builderForm.on( 'click', '.frm_mark_unique', markUnique );
5511
5512 $builderForm.on( 'change', '.frm_repeat_format', toggleRepeatButtons );
5513 $builderForm.on( 'change', '.frm_repeat_limit', checkRepeatLimit );
5514 $builderForm.on( 'change', '.frm_js_checkbox_limit', checkCheckboxSelectionsLimit );
5515 $builderForm.on( 'input', 'input[name^="field_options[add_label_"]', function() {
5516 updateRepeatText( this, 'add' );
5517 });
5518 $builderForm.on( 'input', 'input[name^="field_options[remove_label_"]', function() {
5519 updateRepeatText( this, 'remove' );
5520 });
5521 $builderForm.on( 'change', 'select[name^="field_options[data_type_"]', maybeClearWatchFields );
5522 jQuery( builderArea ).on( 'click', '.frm-collapse-page', maybeCollapsePage );
5523 jQuery( builderArea ).on( 'click', '.frm-collapse-section', maybeCollapseSection );
5524 $builderForm.on( 'click', '.frm-single-settings h3', maybeCollapseSettings );
5525
5526 $builderForm.on( 'click', '.frm_toggle_sep_values', toggleSepValues );
5527 $builderForm.on( 'click', '.frm_multiselect_opt', toggleMultiselect );
5528 $newFields.on( 'mousedown', 'input, textarea, select', stopFieldFocus );
5529 $newFields.on( 'click', 'input[type=radio], input[type=checkbox]', stopFieldFocus );
5530 $newFields.on( 'click', '.frm_delete_field', clickDeleteField );
5531 $builderForm.on( 'click', '.frm_single_option a[data-removeid]', deleteFieldOption );
5532 $builderForm.on( 'mousedown', '.frm_single_option input[type=radio]', maybeUncheckRadio );
5533 $builderForm.on( 'focusin', '.frm_single_option input[type=text]', maybeClearOptText );
5534 $builderForm.on( 'click', '.frm_add_opt', addFieldOption );
5535 $builderForm.on( 'change', '.frm_single_option input', resetOptOnChange );
5536 $builderForm.on( 'change', '.frm_toggle_mult_sel', toggleMultSel );
5537 $builderForm.on( 'focusin', '.frm_classes', showBuilderModal );
5538
5539 $newFields.on( 'click', '.frm_primary_label', clickLabel );
5540 $newFields.on( 'click', '.frm_description', clickDescription );
5541 $newFields.on( 'click', 'li.ui-state-default', clickVis );
5542 $newFields.on( 'dblclick', 'li.ui-state-default', openAdvanced );
5543 $builderForm.on( 'change', '.frm_tax_form_select', toggleFormTax );
5544 $builderForm.on( 'change', 'select.conf_field', addConf );
5545
5546 $builderForm.on( 'change', '.frm_get_field_selection', getFieldSelection );
5547
5548 $builderForm.on( 'click', '.frm-show-inline-modal', maybeShowInlineModal );
5549
5550 $builderForm.on( 'click', '.frm-inline-modal .dismiss', dismissInlineModal );
5551 jQuery( document ).on( 'change', '[data-frmchange]', changeInputtedValue );
5552
5553 $builderForm.on( 'change', '.frm_include_extras_field', rePopCalcFieldsForSummary );
5554 $builderForm.on( 'change', 'select[name^="field_options[form_select_"]', maybeChangeEmbedFormMsg );
5555
5556 popAllProductFields();
5557
5558 jQuery( document ).on( 'change', '.frmjs_prod_data_type_opt', toggleProductType );
5559
5560 initBulkOptionsOverlay();
5561 hideEmptyEle();
5562 maybeDisableAddSummaryBtn();
5563 maybeHideQuantityProductFieldOption();
5564 },
5565
5566 settingsInit: function() {
5567 var formSettings, $loggedIn, $cookieExp, $editable,
5568 $formActions = jQuery( document.getElementById( 'frm_notification_settings' ) );
5569 //BCC, CC, and Reply To button functionality
5570 $formActions.on( 'click', '.frm_email_buttons', showEmailRow );
5571 $formActions.on( 'click', '.frm_remove_field', hideEmailRow );
5572 $formActions.on( 'change', '.frm_to_row, .frm_from_row', showEmailWarning );
5573 $formActions.on( 'change', '.frm_tax_selector', changePosttaxRow );
5574 $formActions.on( 'change', 'select.frm_single_post_field', checkDupPost );
5575 $formActions.on( 'change', 'select.frm_toggle_post_content', togglePostContent );
5576 $formActions.on( 'change', 'select.frm_dyncontent_opt', fillDyncontent );
5577 $formActions.on( 'change', '.frm_post_type', switchPostType );
5578 $formActions.on( 'click', '.frm_add_postmeta_row', addPostmetaRow );
5579 $formActions.on( 'click', '.frm_add_posttax_row', addPosttaxRow );
5580 $formActions.on( 'click', '.frm_toggle_cf_opts', toggleCfOpts );
5581 $formActions.on( 'click', '.frm_duplicate_form_action', copyFormAction );
5582 jQuery( 'select[data-toggleclass], input[data-toggleclass]' ).change( toggleFormOpts );
5583 jQuery( '.frm_actions_list' ).on( 'click', '.frm_active_action', addFormAction );
5584 jQuery( '#frm-show-groups, #frm-hide-groups' ).click( toggleActionGroups );
5585 initiateMultiselect();
5586
5587 //set actions icons to inactive
5588 jQuery( 'ul.frm_actions_list li' ).each( function() {
5589 checkActiveAction( jQuery( this ).children( 'a' ).data( 'actiontype' ) );
5590
5591 // If the icon is a background image, don't add BG color.
5592 var icon = jQuery( this ).find( 'i' );
5593 if ( icon.css( 'background-image' ) !== 'none' ) {
5594 icon.addClass( 'frm-inverse' );
5595 }
5596 });
5597
5598 jQuery( '.frm_submit_settings_btn' ).click( submitSettings );
5599
5600 formSettings = jQuery( '.frm_form_settings' );
5601 formSettings.on( 'click', '.frm_add_form_logic', addFormLogicRow );
5602 formSettings.on( 'blur', '.frm_email_blur', formatEmailSetting );
5603 formSettings.on( 'click', '.frm_already_used', onlyOneActionMessage );
5604
5605 formSettings.on( 'change', '#logic_link_submit', toggleSubmitLogic );
5606 formSettings.on( 'click', '.frm_add_submit_logic', addSubmitLogic );
5607 formSettings.on( 'change', '.frm_submit_logic_field_opts', addSubmitLogicOpts );
5608
5609
5610 // Close shortcode modal on click.
5611 formSettings.on( 'mouseup', '*:not(.frm-show-box)', function( e ) {
5612 e.stopPropagation();
5613 if ( e.target.classList.contains( 'frm-show-box' ) ) {
5614 return;
5615 }
5616 var sidebar = document.getElementById( 'frm_adv_info' ),
5617 isChild = jQuery( e.target ).closest( '#frm_adv_info' ).length > 0;
5618
5619 if ( sidebar.getAttribute( 'data-fills' ) === e.target.id && typeof e.target.id !== 'undefined' ) {
5620 return;
5621 }
5622
5623 if ( sidebar !== null && ! isChild && sidebar.display !== 'none' ) {
5624 hideShortcodes( sidebar );
5625 }
5626 });
5627
5628 //Warning when user selects "Do not store entries ..."
5629 jQuery( document.getElementById( 'no_save' ) ).change( function() {
5630 if ( this.checked ) {
5631 if ( confirm( frm_admin_js.no_save_warning ) !== true ) {
5632 // Uncheck box if user hits "Cancel"
5633 jQuery( this ).attr( 'checked', false );
5634 }
5635 }
5636 });
5637
5638 //Show/hide Messages header
5639 jQuery( '#editable, #edit_action, #save_draft, #success_action' ).change( function() {
5640 maybeShowFormMessages();
5641 });
5642 jQuery( 'select[name="options[success_action]"], select[name="options[edit_action]"]' ).change( showSuccessOpt );
5643
5644 $loggedIn = document.getElementById( 'logged_in' );
5645 jQuery( $loggedIn ).change( function() {
5646 if ( this.checked ) {
5647 visible( '.hide_logged_in' );
5648 } else {
5649 invisible( '.hide_logged_in' );
5650 }
5651 });
5652
5653 $cookieExp = jQuery( document.getElementById( 'frm_cookie_expiration' ) );
5654 jQuery( document.getElementById( 'frm_single_entry_type' ) ).change( function() {
5655 if ( this.value === 'cookie' ) {
5656 $cookieExp.fadeIn( 'slow' );
5657 } else {
5658 $cookieExp.fadeOut( 'slow' );
5659 }
5660 });
5661
5662 var $singleEntry = document.getElementById( 'single_entry' );
5663 jQuery( $singleEntry ).change( function() {
5664 if ( this.checked ) {
5665 visible( '.hide_single_entry' );
5666 } else {
5667 invisible( '.hide_single_entry' );
5668 }
5669
5670 if ( this.checked && jQuery( document.getElementById( 'frm_single_entry_type' ) ).val() === 'cookie' ) {
5671 $cookieExp.fadeIn( 'slow' );
5672 } else {
5673 $cookieExp.fadeOut( 'slow' );
5674 }
5675 });
5676
5677 jQuery( '.hide_save_draft' ).hide();
5678
5679 var $saveDraft = jQuery( document.getElementById( 'save_draft' ) );
5680 $saveDraft.change( function() {
5681 if ( this.checked ) {
5682 jQuery( '.hide_save_draft' ).fadeIn( 'slow' );
5683 } else {
5684 jQuery( '.hide_save_draft' ).fadeOut( 'slow' );
5685 }
5686 });
5687 $saveDraft.change();
5688
5689 //If Allow editing is checked/unchecked
5690 $editable = document.getElementById( 'editable' );
5691 jQuery( $editable ).change( function() {
5692 if ( this.checked ) {
5693 jQuery( '.hide_editable' ).fadeIn( 'slow' );
5694 jQuery( '#edit_action' ).change();
5695 } else {
5696 jQuery( '.hide_editable' ).fadeOut( 'slow' );
5697 jQuery( '.edit_action_message_box' ).fadeOut( 'slow' );//Hide On Update message box
5698 }
5699 });
5700
5701 // Page Selection Autocomplete
5702 initSelectionAutocomplete();
5703 },
5704
5705 panelInit: function() {
5706 var customPanel, settingsPage, viewPage, insertFieldsTab;
5707
5708 jQuery( '.frm_wrap, #postbox-container-1' ).on( 'click', '.frm_insert_code', insertCode );
5709 jQuery( document ).on( 'change', '.frm_insert_val', function() {
5710 insertFieldCode( jQuery( this ).data( 'target' ), jQuery( this ).val() );
5711 jQuery( this ).val( '' );
5712 });
5713
5714 jQuery( document ).on( 'click change', '#frm-id-key-condition', resetLogicBuilder );
5715 jQuery( document ).on( 'keyup change', '.frm-build-logic', setLogicExample );
5716
5717 showInputIcon();
5718 jQuery( document ).on( 'frmElementAdded', function( event, parentEle ) {
5719 /* This is here for add-ons to trigger */
5720 showInputIcon( parentEle );
5721 });
5722 jQuery( document ).on( 'mousedown', '.frm-show-box', showShortcodes );
5723
5724 settingsPage = document.getElementById( 'form_settings_page' );
5725 viewPage = document.body.classList.contains( 'post-type-frm_display' );
5726 insertFieldsTab = document.getElementById( 'frm_insert_fields_tab' );
5727
5728 if ( settingsPage !== null || viewPage ) {
5729 jQuery( document ).on( 'focusin', 'form input, form textarea', function( e ) {
5730 var htmlTab;
5731 e.stopPropagation();
5732 maybeShowModal( this );
5733
5734 if ( jQuery( this ).is( ':not(:submit, input[type=button], .frm-search-input, input[type=checkbox])' ) ) {
5735 if ( jQuery( e.target ).closest( '#frm_adv_info' ).length ) {
5736 // Don't trigger for fields inside of the modal.
5737 return;
5738 }
5739
5740 if ( settingsPage !== null ) {
5741 /* form settings page */
5742 htmlTab = jQuery( '#frm_html_tab' );
5743 if ( jQuery( this ).closest( '#html_settings' ).length > 0 ) {
5744 htmlTab.show();
5745 htmlTab.siblings().hide();
5746 jQuery( '#frm_html_tab a' ).click();
5747 toggleAllowedHTML( this, e.type );
5748 } else {
5749 showElement( jQuery( '.frm-category-tabs li' ) );
5750 insertFieldsTab.click();
5751 htmlTab.hide();
5752 htmlTab.siblings().show();
5753 }
5754 } else if ( viewPage ) {
5755 // Run on view page.
5756 toggleAllowedShortcodes( this.id, e.type );
5757 }
5758 }
5759 });
5760 }
5761
5762 jQuery( '.frm_wrap, #postbox-container-1' ).on( 'mousedown', '#frm_adv_info a, .frm_field_list a', function( e ) {
5763 e.preventDefault();
5764 });
5765
5766 customPanel = jQuery( '#frm_adv_info' );
5767 customPanel.on( 'click', '.subsubsub a.frmids', function( e ) {
5768 toggleKeyID( 'frmids', e );
5769 });
5770 customPanel.on( 'click', '.subsubsub a.frmkeys', function( e ) {
5771 toggleKeyID( 'frmkeys', e );
5772 });
5773 },
5774
5775 templateInit: function() {
5776 initTemplateModal();
5777 initiateMultiselect();
5778 },
5779
5780 viewInit: function() {
5781 var $addRemove,
5782 $advInfo = jQuery( document.getElementById( 'frm_adv_info' ) );
5783 $advInfo.before( '<div id="frm_position_ele"></div>' );
5784 setupMenuOffset();
5785
5786 jQuery( document ).on( 'blur', '#param', checkDetailPageSlug );
5787 jQuery( document ).on( 'blur', 'input[name^="options[where_val]"]', checkFilterParamNames );
5788
5789 // Show loading indicator.
5790 jQuery( '#publish' ).mousedown( function() {
5791 this.classList.add( 'frm_loading_button' );
5792 });
5793
5794 // move content tabs
5795 jQuery( '#frm_dyncontent .handlediv' ).before( jQuery( '#frm_dyncontent .nav-menus-php' ) );
5796
5797 // click content tabs
5798 jQuery( '.nav-tab-wrapper a' ).click( clickContentTab );
5799
5800 // click tabs after panel is replaced with ajax
5801 jQuery( '#side-sortables' ).on( 'click', '.frm_doing_ajax.categorydiv .category-tabs a', clickTabsAfterAjax );
5802
5803 initToggleShortcodes();
5804 jQuery( '.frm_code_list:not(.frm-dropdown-menu) a' ).addClass( 'frm_noallow' );
5805
5806 jQuery( 'input[name="show_count"]' ).change( showCount );
5807
5808 jQuery( document.getElementById( 'form_id' ) ).change( displayFormSelected );
5809
5810 $addRemove = jQuery( '.frm_repeat_rows' );
5811 $addRemove.on( 'click', '.frm_add_order_row', addOrderRow );
5812 $addRemove.on( 'click', '.frm_add_where_row', addWhereRow );
5813 $addRemove.on( 'change', '.frm_insert_where_options', insertWhereOptions );
5814 $addRemove.on( 'change', '.frm_where_is_options', hideWhereOptions );
5815
5816 setDefaultPostStatus();
5817 },
5818
5819 inboxInit: function() {
5820 jQuery( '.frm_inbox_dismiss, footer .frm-button-secondary, footer .frm-button-primary' ).click( function( e ) {
5821 var message = this.parentNode.parentNode,
5822 key = message.getAttribute( 'data-message' ),
5823 href = this.getAttribute( 'href' );
5824
5825 e.preventDefault();
5826
5827 data = {
5828 action: 'frm_inbox_dismiss',
5829 key: key,
5830 nonce: frmGlobal.nonce
5831 };
5832 postAjax( data, function() {
5833 if ( href !== '#' ) {
5834 window.location = href;
5835 return true;
5836 }
5837 fadeOut( message, function() {
5838 message.parentNode.removeChild( message );
5839 });
5840 });
5841 });
5842 },
5843
5844 styleInit: function() {
5845 collapseAllSections();
5846
5847 document.getElementById( 'frm_field_height' ).addEventListener( 'change', textSquishCheck );
5848 document.getElementById( 'frm_field_font_size' ).addEventListener( 'change', textSquishCheck );
5849 document.getElementById( 'frm_field_pad' ).addEventListener( 'change', textSquishCheck );
5850
5851 jQuery( 'input.hex' ).wpColorPicker({
5852 change: function( event ) {
5853 var hexcolor = jQuery( this ).wpColorPicker( 'color' );
5854 jQuery( event.target ).val( hexcolor ).change();
5855 }
5856 });
5857 jQuery( '.wp-color-result-text' ).text( function( i, oldText ) {
5858 return oldText === 'Select Color' ? 'Select' : oldText;
5859 });
5860
5861 // update styling on change
5862 jQuery( '#frm_styling_form .styling_settings' ).change( function() {
5863 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();
5864 locStr = JSON.stringify( locStr );
5865 jQuery.ajax({
5866 type: 'POST', url: ajaxurl,
5867 data: {
5868 action: 'frm_change_styling',
5869 nonce: frmGlobal.nonce,
5870 frm_style_setting: locStr
5871 },
5872 success: function( css ) {
5873 document.getElementById( 'this_css' ).innerHTML = css;
5874 }
5875 });
5876 });
5877
5878 // menu tabs
5879 jQuery( '#menu-settings-column' ).bind( 'click', function( e ) {
5880 var panelId, wrapper,
5881 target = jQuery( e.target );
5882
5883 if ( e.target.className.indexOf( 'nav-tab-link' ) !== -1 ) {
5884
5885 panelId = target.data( 'type' );
5886
5887 wrapper = target.parents( '.accordion-section-content' ).first();
5888
5889
5890 jQuery( '.tabs-panel-active', wrapper ).removeClass( 'tabs-panel-active' ).addClass( 'tabs-panel-inactive' );
5891 jQuery( '#' + panelId, wrapper ).removeClass( 'tabs-panel-inactive' ).addClass( 'tabs-panel-active' );
5892
5893 jQuery( '.tabs', wrapper ).removeClass( 'tabs' );
5894 target.parent().addClass( 'tabs' );
5895
5896 // select the search bar
5897 jQuery( '.quick-search', wrapper ).focus();
5898
5899 e.preventDefault();
5900 }
5901 });
5902
5903 jQuery( '.multiselect-container.frm-dropdown-menu li a' ).click( function() {
5904 var radio = this.children[0].children[0];
5905 var btnGrp = jQuery( this ).closest( '.btn-group' );
5906 var btnId = btnGrp.attr( 'id' );
5907 document.getElementById( btnId.replace( '_select', '' ) ).value = radio.value;
5908 btnGrp.children( 'button' ).html( radio.nextElementSibling.innerHTML + ' <b class="caret"></b>' );
5909
5910 // set active class
5911 btnGrp.find( 'li.active' ).removeClass( 'active' );
5912 jQuery( this ).closest( 'li' ).addClass( 'active' );
5913 });
5914
5915 jQuery( '#frm_confirm_modal' ).on( 'click', '[data-resetstyle]', function( e ) {
5916 var button = document.getElementById( 'frm_reset_style' );
5917
5918 button.classList.add( 'frm_loading_button' );
5919 e.stopPropagation();
5920
5921 jQuery.ajax({
5922 type: 'POST', url: ajaxurl,
5923 data: {action: 'frm_settings_reset', nonce: frmGlobal.nonce},
5924 success: function( errObj ) {
5925 var key;
5926 errObj = errObj.replace( /^\s+|\s+$/g, '' );
5927 if ( errObj.indexOf( '{' ) === 0 ) {
5928 errObj = jQuery.parseJSON( errObj );
5929 }
5930 for ( key in errObj ) {
5931 jQuery( 'input[name$="[' + key + ']"], select[name$="[' + key + ']"]' ).val( errObj[key]);
5932 }
5933 jQuery( '#frm_submit_style, #frm_auto_width' ).prop( 'checked', false );
5934 jQuery( document.getElementById( 'frm_fieldset' ) ).change();
5935 button.classList.remove( 'frm_loading_button' );
5936 }
5937 });
5938 });
5939
5940 jQuery( '.frm_pro_form #datepicker_sample' ).datepicker({ changeMonth: true, changeYear: true });
5941
5942 jQuery( document.getElementById( 'frm_position' ) ).change( setPosClass );
5943
5944 jQuery( 'select[name$="[theme_selector]"]' ).change( function() {
5945 var themeVal = jQuery( this ).val();
5946 var css = themeVal;
5947 if ( themeVal !== -1 ) {
5948 if ( themeVal === 'ui-lightness' && frm_admin_js.pro_url !== '' ) {
5949 css = frm_admin_js.pro_url + '/css/ui-lightness/jquery-ui.css';
5950 jQuery( '.frm_date_color' ).show();
5951 } else {
5952 css = frm_admin_js.jquery_ui_url + '/themes/' + themeVal + '/jquery-ui.css';
5953 jQuery( '.frm_date_color' ).hide();
5954 }
5955 }
5956
5957 updateUICSS( css );
5958 document.getElementById( 'frm_theme_css' ).value = themeVal;
5959 return false;
5960 }).change();
5961 },
5962
5963 customCSSInit: function() {
5964 /* deprecated since WP 4.9 */
5965 var customCSS = document.getElementById( 'frm_custom_css_box' );
5966 if ( customCSS !== null ) {
5967 CodeMirror.fromTextArea( customCSS, {
5968 lineNumbers: true
5969 });
5970 }
5971 },
5972
5973 globalSettingsInit: function() {
5974 var licenseTab;
5975
5976 jQuery( document ).on( 'click', '[data-frmuninstall]', uninstallNow );
5977
5978 initiateMultiselect();
5979
5980 // activate addon licenses
5981 licenseTab = document.getElementById( 'licenses_settings' );
5982 if ( licenseTab !== null ) {
5983 jQuery( licenseTab ).on( 'click', '.edd_frm_save_license', saveAddonLicense );
5984 }
5985
5986 jQuery( '#frm-dismissable-cta .dismiss' ).click( function( event ) {
5987 event.preventDefault();
5988 jQuery.post( ajaxurl, {
5989 action: 'frm_lite_settings_upgrade'
5990 });
5991 jQuery( '.settings-lite-cta' ).remove();
5992 });
5993 },
5994
5995 exportInit: function() {
5996 jQuery( '.frm_form_importer' ).submit( startFormMigration );
5997 jQuery( document.getElementById( 'frm_export_xml' ) ).submit( validateExport );
5998 jQuery( '#frm_export_xml input, #frm_export_xml select' ).change( removeExportError );
5999 jQuery( 'input[name="frm_import_file"]' ).change( checkCSVExtension );
6000 jQuery( 'select[name="format"]' ).change( checkExportTypes ).change();
6001 jQuery( 'input[name="frm_export_forms[]"]' ).click( preventMultipleExport );
6002 initiateMultiselect();
6003
6004 jQuery( '.frm-feature-banner .dismiss' ).click( function( event ) {
6005 event.preventDefault();
6006 jQuery.post( ajaxurl, {
6007 action: 'frm_dismiss_migrator',
6008 plugin: this.id,
6009 nonce: frmGlobal.nonce
6010 });
6011 this.parentElement.remove();
6012 });
6013 },
6014
6015 updateOpts: function( fieldId, opts, modal ) {
6016 var separate = usingSeparateValues( fieldId ),
6017 action = isProductField( fieldId ) ? 'frm_bulk_products' : 'frm_import_options';
6018 jQuery.ajax({
6019 type: 'POST',
6020 url: ajaxurl,
6021 data: {
6022 action: action,
6023 field_id: fieldId,
6024 opts: opts,
6025 separate: separate,
6026 nonce: frmGlobal.nonce
6027 },
6028 success: function( html ) {
6029 document.getElementById( 'frm_field_' + fieldId + '_opts' ).innerHTML = html;
6030 resetDisplayedOpts( fieldId );
6031
6032 if ( typeof modal !== 'undefined' ) {
6033 modal.dialog( 'close' );
6034 document.getElementById( 'frm-update-bulk-opts' ).classList.remove( 'frm_loading_button' );
6035 }
6036 }
6037 });
6038 },
6039
6040 /* remove conditional logic if the field doesn't exist */
6041 triggerRemoveLogic: function( fieldID, metaName ) {
6042 jQuery( '#frm_logic_' + fieldID + '_' + metaName + ' .frm_remove_tag' ).click();
6043 },
6044
6045 downloadXML: function( controller, ids, isTemplate ) {
6046 var url = ajaxurl + '?action=frm_' + controller + '_xml&ids=' + ids;
6047 if ( isTemplate !== null ) {
6048 url = url + '&is_template=' + isTemplate;
6049 }
6050 location.href = url;
6051 }
6052 };
6053 }
6054
6055 frmAdminBuild = frmAdminBuildJS();
6056
6057 jQuery( document ).ready( function( $ ) {
6058 frmAdminBuild.init();
6059 });
6060
6061 function frm_remove_tag( htmlTag ) { // eslint-disable-line camelcase
6062 console.warn( 'DEPRECATED: function frm_remove_tag in v2.0' );
6063 jQuery( htmlTag ).remove();
6064 }
6065
6066 function frm_show_div( div, value, showIf, classId ) { // eslint-disable-line camelcase
6067 if ( value == showIf ) {
6068 jQuery( classId + div ).fadeIn( 'slow' ).css( 'visibility', 'visible' );
6069 } else {
6070 jQuery( classId + div ).fadeOut( 'slow' );
6071 }
6072 }
6073
6074 function frmCheckAll( checked, n ) {
6075 if ( checked ) {
6076 jQuery( 'input[name^="' + n + '"]' ).attr( 'checked', 'checked' );
6077 } else {
6078 jQuery( 'input[name^="' + n + '"]' ).removeAttr( 'checked' );
6079 }
6080 }
6081
6082 function frmCheckAllLevel( checked, n, level ) {
6083 var $kids = jQuery( '.frm_catlevel_' + level ).children( '.frm_checkbox' ).children( 'label' );
6084 if ( checked ) {
6085 $kids.children( 'input[name^="' + n + '"]' ).attr( 'checked', 'checked' );
6086 } else {
6087 $kids.children( 'input[name^="' + n + '"]' ).removeAttr( 'checked' );
6088 }
6089 }
6090
6091 function frm_add_logic_row( id, formId ) { // eslint-disable-line camelcase
6092 console.warn( 'DEPRECATED: function frm_add_logic_row in v2.0' );
6093 jQuery.ajax({
6094 type: 'POST',
6095 url: ajaxurl,
6096 data: {
6097 action: 'frm_add_logic_row',
6098 form_id: formId,
6099 field_id: id,
6100 meta_name: jQuery( '#frm_logic_row_' + id + ' > div' ).size(),
6101 nonce: frmGlobal.nonce
6102 },
6103 success: function( html ) {
6104 jQuery( '#frm_logic_row_' + id ).append( html );
6105 }
6106 });
6107 return false;
6108 }
6109
6110 function frmGetFieldValues( fieldId, cur, rowNumber, fieldType, htmlName ) {
6111
6112 if ( fieldId ) {
6113 jQuery.ajax({
6114 type: 'POST', url: ajaxurl,
6115 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,
6116 success: function( msg ) {
6117 document.getElementById( 'frm_show_selected_values_' + cur + '_' + rowNumber ).innerHTML = msg;
6118 }
6119 });
6120 }
6121 }
6122
6123 function frmImportCsv( formID ) {
6124 var urlVars = '';
6125 if ( typeof __FRMURLVARS !== 'undefined' ) {
6126 urlVars = __FRMURLVARS;
6127 }
6128
6129 jQuery.ajax({
6130 type: 'POST', url: ajaxurl,
6131 data: 'action=frm_import_csv&nonce=' + frmGlobal.nonce + '&frm_skip_cookie=1' + urlVars,
6132 success: function( count ) {
6133 var max = jQuery( '.frm_admin_progress_bar' ).attr( 'aria-valuemax' );
6134 var imported = max - count;
6135 var percent = ( imported / max ) * 100;
6136 jQuery( '.frm_admin_progress_bar' ).css( 'width', percent + '%' ).attr( 'aria-valuenow', imported );
6137
6138 if ( parseInt( count, 10 ) > 0 ) {
6139 jQuery( '.frm_csv_remaining' ).html( count );
6140 frmImportCsv( formID );
6141 } else {
6142 jQuery( document.getElementById( 'frm_import_message' ) ).html( frm_admin_js.import_complete );
6143 setTimeout( function() {
6144 location.href = '?page=formidable-entries&frm_action=list&form=' + formID + '&import-message=1';
6145 }, 2000 );
6146 }
6147 }
6148 });
6149 }
6150
6151 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim#Polyfill
6152 if ( ! String.prototype.trim ) {
6153 String.prototype.trim = function() {
6154 return this.replace( /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '' );
6155 };
6156 }
6157 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith#Polyfill
6158 if ( ! String.prototype.startsWith ) {
6159 Object.defineProperty( String.prototype, 'startsWith', {
6160 value: function( search, pos ) {
6161 pos = ! pos || pos < 0 ? 0 : +pos;
6162 return this.substring( pos, pos + search.length ) === search;
6163 }
6164 });
6165 }
6166