admin/admin-editor.js000064400000003040151716470030010547 0ustar00;( function( $ ) { $( function() { // Close modal. var wpformsModalClose = function() { if ( $( '#wpforms-modal-select-form' ).length ) { $( '#wpforms-modal-select-form' ).get( 0 ).selectedIndex = 0; $( '#wpforms-modal-checkbox-title, #wpforms-modal-checkbox-description' ).prop( 'checked', false ); } $( '#wpforms-modal-backdrop, #wpforms-modal-wrap' ).css( 'display', 'none' ); $( document.body ).removeClass( 'modal-open' ); }; // Open modal when media button is clicked. $( document ).on( 'click', '.wpforms-insert-form-button', function( event ) { event.preventDefault(); $( '#wpforms-modal-backdrop, #wpforms-modal-wrap' ).css( 'display', 'block' ); $( document.body ).addClass( 'modal-open' ); } ); // Close modal on close or cancel links. $( document ).on( 'click', '#wpforms-modal-close, #wpforms-modal-cancel a', function( event ) { event.preventDefault(); wpformsModalClose(); } ); // Insert shortcode into TinyMCE. $( document ).on( 'click', '#wpforms-modal-submit', function( event ) { event.preventDefault(); var shortcode; shortcode = '[wpforms id="' + $( '#wpforms-modal-select-form' ).val() + '"'; if ( $( '#wpforms-modal-checkbox-title' ).is( ':checked' ) ) { shortcode = shortcode + ' title="true"'; } if ( $( '#wpforms-modal-checkbox-description' ).is( ':checked' ) ) { shortcode = shortcode + ' description="true"'; } shortcode = shortcode + ']'; wp.media.editor.insert( shortcode ); wpformsModalClose(); } ); } ); }( jQuery ) ); admin/admin-editor.min.js000064400000001741151716470030011337 0ustar00!function(c){c(function(){function e(){c("#wpforms-modal-select-form").length&&(c("#wpforms-modal-select-form").get(0).selectedIndex=0,c("#wpforms-modal-checkbox-title, #wpforms-modal-checkbox-description").prop("checked",!1)),c("#wpforms-modal-backdrop, #wpforms-modal-wrap").css("display","none"),c(document.body).removeClass("modal-open")}c(document).on("click",".wpforms-insert-form-button",function(o){o.preventDefault(),c("#wpforms-modal-backdrop, #wpforms-modal-wrap").css("display","block"),c(document.body).addClass("modal-open")}),c(document).on("click","#wpforms-modal-close, #wpforms-modal-cancel a",function(o){o.preventDefault(),e()}),c(document).on("click","#wpforms-modal-submit",function(o){o.preventDefault(),o='[wpforms id="'+c("#wpforms-modal-select-form").val()+'"',c("#wpforms-modal-checkbox-title").is(":checked")&&(o+=' title="true"'),c("#wpforms-modal-checkbox-description").is(":checked")&&(o+=' description="true"'),o+="]",wp.media.editor.insert(o),e()})})}(jQuery);admin/admin-notifications.js000064400000007504151716470030012143 0ustar00/* global wpforms_admin, WPFormsAdmin */ /** * WPForms Admin Notifications. * * @since 1.6.0 */ 'use strict'; var WPFormsAdminNotifications = window.WPFormsAdminNotifications || ( function( document, window, $ ) { /** * Elements holder. * * @since 1.6.0 * * @type {object} */ var el = { $notifications: $( '#wpforms-notifications' ), $nextButton: $( '#wpforms-notifications .navigation .next' ), $prevButton: $( '#wpforms-notifications .navigation .prev' ), $adminBarCounter: $( '#wp-admin-bar-wpforms-menu .wpforms-menu-notification-counter' ), $adminBarMenuItem: $( '#wp-admin-bar-wpforms-notifications' ), }; /** * Public functions and properties. * * @since 1.6.0 * * @type {object} */ var app = { /** * Start the engine. * * @since 1.6.0 */ init: function() { $( app.ready ); }, /** * Document ready. * * @since 1.6.0 */ ready: function() { app.updateNavigation(); app.events(); }, /** * Register JS events. * * @since 1.6.0 */ events: function() { el.$notifications .on( 'click', '.dismiss', app.dismiss ) .on( 'click', '.next', app.navNext ) .on( 'click', '.prev', app.navPrev ); }, /** * Click on the Dismiss notification button. * * @since 1.6.0 * * @param {object} event Event object. */ dismiss: function( event ) { if ( el.$currentMessage.length === 0 ) { return; } // Update counter. var count = parseInt( el.$adminBarCounter.text(), 10 ); if ( count > 1 ) { --count; el.$adminBarCounter.html( count ); } else { el.$adminBarCounter.remove(); el.$adminBarMenuItem.remove(); } // Remove notification. var $nextMessage = el.$nextMessage.length < 1 ? el.$prevMessage : el.$nextMessage, messageId = el.$currentMessage.data( 'message-id' ); if ( $nextMessage.length === 0 ) { el.$notifications.remove(); } else { el.$currentMessage.remove(); $nextMessage.addClass( 'current' ); app.updateNavigation(); } // AJAX call - update option. var data = { action: 'wpforms_notification_dismiss', nonce: wpforms_admin.nonce, id: messageId, }; $.post( wpforms_admin.ajax_url, data, function( res ) { if ( ! res.success ) { WPFormsAdmin.debug( res ); } } ).fail( function( xhr, textStatus, e ) { WPFormsAdmin.debug( xhr.responseText ); } ); }, /** * Click on the Next notification button. * * @since 1.6.0 * * @param {object} event Event object. */ navNext: function( event ) { if ( el.$nextButton.hasClass( 'disabled' ) ) { return; } el.$currentMessage.removeClass( 'current' ); el.$nextMessage.addClass( 'current' ); app.updateNavigation(); }, /** * Click on the Previous notification button. * * @since 1.6.0 * * @param {object} event Event object. */ navPrev: function( event ) { if ( el.$prevButton.hasClass( 'disabled' ) ) { return; } el.$currentMessage.removeClass( 'current' ); el.$prevMessage.addClass( 'current' ); app.updateNavigation(); }, /** * Update navigation buttons. * * @since 1.6.0 */ updateNavigation: function() { el.$currentMessage = el.$notifications.find( '.wpforms-notifications-message.current' ); el.$nextMessage = el.$currentMessage.next( '.wpforms-notifications-message' ); el.$prevMessage = el.$currentMessage.prev( '.wpforms-notifications-message' ); if ( el.$nextMessage.length === 0 ) { el.$nextButton.addClass( 'disabled' ); } else { el.$nextButton.removeClass( 'disabled' ); } if ( el.$prevMessage.length === 0 ) { el.$prevButton.addClass( 'disabled' ); } else { el.$prevButton.removeClass( 'disabled' ); } }, }; return app; }( document, window, jQuery ) ); // Initialize. WPFormsAdminNotifications.init(); admin/admin-notifications.min.js000064400000004006151716470030012717 0ustar00"use strict";var WPFormsAdminNotifications=window.WPFormsAdminNotifications||function(t){var i={$notifications:t("#wpforms-notifications"),$nextButton:t("#wpforms-notifications .navigation .next"),$prevButton:t("#wpforms-notifications .navigation .prev"),$adminBarCounter:t("#wp-admin-bar-wpforms-menu .wpforms-menu-notification-counter"),$adminBarMenuItem:t("#wp-admin-bar-wpforms-notifications")},a={init:function(){t(a.ready)},ready:function(){a.updateNavigation(),a.events()},events:function(){i.$notifications.on("click",".dismiss",a.dismiss).on("click",".next",a.navNext).on("click",".prev",a.navPrev)},dismiss:function(e){var n,s;0!==i.$currentMessage.length&&(1<(s=parseInt(i.$adminBarCounter.text(),10))?i.$adminBarCounter.html(--s):(i.$adminBarCounter.remove(),i.$adminBarMenuItem.remove()),s=i.$nextMessage.length<1?i.$prevMessage:i.$nextMessage,n=i.$currentMessage.data("message-id"),0===s.length?i.$notifications.remove():(i.$currentMessage.remove(),s.addClass("current"),a.updateNavigation()),s={action:"wpforms_notification_dismiss",nonce:wpforms_admin.nonce,id:n},t.post(wpforms_admin.ajax_url,s,function(e){e.success||WPFormsAdmin.debug(e)}).fail(function(e,n,s){WPFormsAdmin.debug(e.responseText)}))},navNext:function(e){i.$nextButton.hasClass("disabled")||(i.$currentMessage.removeClass("current"),i.$nextMessage.addClass("current"),a.updateNavigation())},navPrev:function(e){i.$prevButton.hasClass("disabled")||(i.$currentMessage.removeClass("current"),i.$prevMessage.addClass("current"),a.updateNavigation())},updateNavigation:function(){i.$currentMessage=i.$notifications.find(".wpforms-notifications-message.current"),i.$nextMessage=i.$currentMessage.next(".wpforms-notifications-message"),i.$prevMessage=i.$currentMessage.prev(".wpforms-notifications-message"),0===i.$nextMessage.length?i.$nextButton.addClass("disabled"):i.$nextButton.removeClass("disabled"),0===i.$prevMessage.length?i.$prevButton.addClass("disabled"):i.$prevButton.removeClass("disabled")}};return a}((document,window,jQuery));WPFormsAdminNotifications.init();admin/admin.js000064400000230231151716470030007267 0ustar00/* global wpforms_admin, jconfirm, wpCookies, Choices, List, wpf */ /** * @param wpforms_admin.recreating * @param wpforms_admin.testing */ ( function( $ ) { 'use strict'; // Global settings access. var s; // Admin object. var WPFormsAdmin = { // Settings. settings: { iconActivate: '', iconDeactivate: '', iconInstall: '', iconSpinner: '', mediaFrame: false, }, /** * Start the engine. * * @since 1.3.9 */ init: function() { // Settings shortcut. s = this.settings; // Document ready. $( WPFormsAdmin.ready ); // Entries Single (Details). WPFormsAdmin.initEntriesSingle(); // Entries List. WPFormsAdmin.initEntriesList(); // Welcome activation. WPFormsAdmin.initWelcome(); // Addons List. $( document ).on( 'wpformsReady', WPFormsAdmin.initAddons ); // Settings. WPFormsAdmin.initSettings(); // Tools. WPFormsAdmin.initTools(); // Upgrades (Tools view). WPFormsAdmin.initUpgrades(); // Tab menu. WPFormsAdmin.initScrollableMenu(); }, /** * Document ready. * * @since 1.3.9 */ ready: function() { // Add `_wp_http_referer` to the data of every AJAX request. $.ajaxSetup( { data: { // eslint-disable-next-line camelcase _wp_http_referer: wpf.updateQueryString( '_wp_http_referer', null ), }, } ); // Scroll to integration. WPFormsAdmin.scrollToIntegration(); // To prevent jumping (since WP core moves the notices with js), // they are hidden initially with CSS, then revealed below with JS, // which runs after they have been moved. $( '.notice' ).show(); // If there are screen options we have to move them. $( '#screen-meta-links, #screen-meta' ).prependTo( '#wpforms-header-temp' ).show(); // Init fancy selects via choices.js. WPFormsAdmin.initChoicesJS(); // Reinit ChoicesJS after htmx swap. $( document ).on( 'htmx:afterSwap', WPFormsAdmin.initChoicesJS ); // Init checkbox multi selects columns. WPFormsAdmin.initCheckboxMultiselectColumns(); // Init color pickers via minicolors.js. $( '.wpforms-color-picker' ).each( function() { const $this = $( this ); $this.minicolors( { defaultValue: $this.data( 'fallback-color' ) || '', } ); } ); // Init fancy File Uploads. $( '.wpforms-file-upload' ).each( function() { var $input = $( this ).find( 'input[type=file]' ), $label = $( this ).find( 'label' ), labelVal = $label.html(); $input.on( 'change', function( event ) { var fileName = ''; if ( this.files && this.files.length > 1 ) { fileName = ( this.getAttribute( 'data-multiple-caption' ) || '' ).replace( '{count}', this.files.length ); } else if ( event.target.value ) { fileName = event.target.value.split( '\\' ).pop(); } if ( fileName ) { $label.find( '.fld' ).html( fileName ); } else { $label.html( labelVal ); } } ); // Firefox bug fix. $input.on( 'focus', function() { $input.addClass( 'has-focus' ); } ).on( 'blur', function() { $input.removeClass( 'has-focus' ); } ); } ); // jquery-confirm defaults. jconfirm.defaults = { closeIcon: false, backgroundDismiss: false, escapeKey: true, animationBounce: 1, useBootstrap: false, theme: 'modern', boxWidth: '400px', animateFromElement: false, content: wpforms_admin.something_went_wrong, }; // Upgrade information modal for upgrade links. $( document ).on( 'click', '.wpforms-upgrade-modal', function() { $.alert( { title: wpforms_admin.thanks_for_interest, content: wpforms_admin.upgrade_modal, icon: 'fa fa-info-circle', type: 'blue', boxWidth: '550px', buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); } ); // Lity lightbox. WPFormsAdmin.initLity(); // Flyout Menu. WPFormsAdmin.initFlyoutMenu(); // Action available for each binding. $( document ).trigger( 'wpformsReady' ); // Start listening for screen options changes. $( '#screen-options-wrap .hide-column-tog' ).on( 'change', WPFormsAdmin.handleOnChangeScreenOptions ); }, /** * For styling purposes, we will add a dedicated class name for determining the number of visible columns. * * @since 1.8.3 */ handleOnChangeScreenOptions: function() { const $table = $( '.wpforms-table-list' ); const $columns = $table.find( 'thead .manage-column' ); const $hidden = $columns.filter( '.hidden' ); const hasManyColumns = Boolean( ( $columns.length - $hidden.length ) > 5 ); // This is used to adjust the table layout. // Add a class to the table to indicate the number of columns. $table.toggleClass( 'has-many-columns', hasManyColumns ); $table.toggleClass( 'has-few-columns', ! hasManyColumns ); }, /** * Initialize Choices JS elements. * * @since 1.4.2 */ initChoicesJS: function() { $( '.choicesjs-select' ).each( function() { var $this = $( this ), args = window.wpforms_admin_choicesjs_config ? { ...window.wpforms_admin_choicesjs_config } : {}; if ( $this.attr( 'multiple' ) ) { args.removeItemButton = typeof args.removeItemButton !== 'undefined' ? args.removeItemButton : true; } if ( $this.data( 'sorting' ) === 'off' ) { args.shouldSort = false; } if ( $this.data( 'search' ) ) { args.searchEnabled = true; } if ( $this.data( 'choices-position' ) ) { args.position = $this.data( 'choices-position' ); } // Render HTML in Choices.js. args.allowHTML = true; // Function to run once Choices initialises. // We need to reproduce a behaviour like on public-facing area for "Edit Entry" page. args.callbackOnInit = function() { const self = this; const $element = $( self.passedElement.element ); const sizeClass = $element.data( 'size-class' ); // Add CSS-class for size. if ( sizeClass ) { $( self.containerOuter.element ).addClass( sizeClass ); } wpf.initMultipleSelectWithSearch( this ); wpf.showMoreButtonForChoices( self.containerOuter.element ); }; $this.data( 'choicesjs', new Choices( $this[ 0 ], args ) ); } ); // Add ability to close the drop-down menu. $( document ).on( 'click', '.choices', function( e ) { const $choices = $( this ), choicesObj = $choices.find( 'select' ).data( 'choicesjs' ); if ( choicesObj && $choices.hasClass( 'is-open' ) && ( e.target.classList.contains( 'choices__inner' ) || e.target.classList.contains( 'choices__arrow' ) ) ) { choicesObj.hideDropdown(); } } ); wpf.initializeChoicesEventHandlers(); }, /** * Initialize checkbox multi-select columns. * * @since 1.4.2 */ initCheckboxMultiselectColumns: function() { $( document ).on( 'change', '.checkbox-multiselect-columns input', function() { var $this = $( this ), $parent = $this.parent(), $container = $this.closest( '.checkbox-multiselect-columns' ), label = $parent.text(), itemID = 'check-item-' + $this.val(), $item = $container.find( '#' + itemID ); if ( $this.prop( 'checked' ) ) { $this.parent().addClass( 'checked' ); if ( ! $item.length ) { $container.find( '.second-column ul' ).append( '
  • ' + label + '
  • ' ); } } else { $this.parent().removeClass( 'checked' ); $container.find( '#' + itemID ).remove(); } } ); $( document ).on( 'click', '.checkbox-multiselect-columns .all', function( event ) { event.preventDefault(); $( this ).closest( '.checkbox-multiselect-columns' ).find( 'input[type=checkbox]' ).prop( 'checked', true ).trigger( 'change' ); $( this ).remove(); } ); }, //--------------------------------------------------------------------// // Forms Overview //--------------------------------------------------------------------// /** * Element bindings for Form Overview page. * * @since 1.3.9 * @since 1.7.3 Deprecated. * * @deprecated Use `WPFormsForms.Overview.init()` instead. */ initFormOverview: function() { console.warn( 'WARNING! Function "WPFormsAdmin.initFormOverview()" has been deprecated, please use the new "WPFormsForms.Overview.init()" function instead!' ); window.WPFormsForms.Overview.init(); }, //--------------------------------------------------------------------// // Entry Single (Details) //--------------------------------------------------------------------// /** * Element bindings for Entries List table page. * * @since 1.3.9 */ initEntriesList() { // eslint-disable-line max-lines-per-function // Toggle form selector dropdown. $( document ).on( 'click', '#wpforms-entries-list .form-selector .toggle', function( event ) { event.preventDefault(); $( this ).toggleClass( 'active' ).next( '.form-list' ).toggle(); } ); // Confirm bulk entry deletion. $( document ).on( 'click', '#wpforms-entries-table #doaction', function( event ) { var $btn = $( this ), $form = $btn.closest( 'form' ), $table = $form.find( 'table' ), $action = $form.find( 'select[name=action]' ), $checked = $table.find( 'input[name^=entry_id]:checked' ); if ( ( 'delete' !== $action.val() && 'trash' !== $action.val() ) || ! $checked.length ) { return; } const $content = 'delete' === $action.val() ? wpforms_admin.entry_delete_n_confirm : wpforms_admin.entry_trash_n_confirm; event.preventDefault(); // Trigger alert modal to confirm. $.confirm( { title: wpforms_admin.heads_up, content: $content.replace( '{entry_count}', $checked.length ), icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action: function() { $form.trigger( 'submit' ); }, }, cancel: { text: wpforms_admin.cancel, keys: [ 'esc' ], }, }, } ); } ); // Confirm entry deletion. $( document ).on( 'click', '#wpforms-entries-list .wp-list-table .delete', function( event ) { event.preventDefault(); var url = $( this ).attr( 'href' ); // Trigger alert modal to confirm. $.confirm( { title: wpforms_admin.heads_up, content: wpforms_admin.entry_delete_confirm, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action: function() { window.location = url; }, }, cancel: { text: wpforms_admin.cancel, keys: [ 'esc' ], }, }, } ); } ); // Confirm entry trash. $( document ).on( 'click', '#wpforms-entries-list .wp-list-table .trash', function( event ) { event.preventDefault(); const url = $( this ).attr( 'href' ); // Trigger alert modal to confirm. $.confirm( { title: wpforms_admin.heads_up, content: wpforms_admin.entry_trash_confirm, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action: () => { window.location = url; }, }, cancel: { text: wpforms_admin.cancel, keys: [ 'esc' ], }, }, } ); } ); // Toggle entry stars. $( document ).on( 'click', '#wpforms-entries-list .wp-list-table .indicator-star', function( event ) { event.preventDefault(); const $this = $( this ); const $counter = $( '#wpforms-entries-list .starred-num' ); const $table = $this.parents( 'table' ); let task = ''; let total = Number( $counter.text() ); if ( $this.hasClass( 'star' ) ) { task = 'star'; total++; $this.attr( 'title', wpforms_admin.entry_unstar ); } else { task = 'unstar'; total--; $this.attr( 'title', wpforms_admin.entry_star ); } $this.toggleClass( 'star unstar' ); if ( ! $table.hasClass( 'wpforms-entries-table-spam' ) && ! $table.hasClass( 'wpforms-entries-table-trash' ) ) { $counter.text( total ); } const data = { task : task, action : 'wpforms_entry_list_star', nonce : wpforms_admin.nonce, entryId : $this.data( 'id' ), formId : $this.data( 'form-id' ), }; $.post( wpforms_admin.ajax_url, data ); } ); // Toggle entry read state. $( document ).on( 'click', '#wpforms-entries-list .wp-list-table .indicator-read', function( event ) { event.preventDefault(); const $this = $( this ); const $counter = $( '#wpforms-entries-list .unread-num' ); const $table = $this.parents( 'table' ); let task = ''; let total = Number( $counter.text() ); if ( $this.hasClass( 'read' ) ) { task = 'read'; total--; $this.attr( 'title', wpforms_admin.entry_unread ); } else { task = 'unread'; total++; $this.attr( 'title', wpforms_admin.entry_read ); } $this.toggleClass( 'read unread' ); if ( ! $table.hasClass( 'wpforms-entries-table-spam' ) && ! $table.hasClass( 'wpforms-entries-table-trash' ) ) { $counter.text( total ); } const data = { task : task, action : 'wpforms_entry_list_read', nonce : wpforms_admin.nonce, entryId : $this.data( 'id' ), formId : $this.data( 'form-id' ), }; $.post( wpforms_admin.ajax_url, data ); } ); // Confirm mass entry deletion/trash - this deletes/trashes ALL entries. $( document ).on( 'click', '#wpforms-entries-list .form-details-actions-removeall', function( event ) { event.preventDefault(); const $page = $( this ).data( 'page' ), $noticeData = WPFormsAdmin.getDeleteAllNoticeData( $page ), $url = $( this ).attr( 'href' ), $table = $( '#wpforms-entries-table' ), filteredCount = $table.data( 'filtered-count-trash' ) && $noticeData.action === 'trash' ? parseInt( $table.data( 'filtered-count-trash' ), 10 ) : 0, data = { action: 'wpforms_entry_list_process_' + $noticeData.action + '_all', form_id: $table.find( 'input[name="form_id"]' ).val(), // eslint-disable-line camelcase date: $table.find( 'input[name="date"]' ).val(), page: $page, search: { field: $table.find( 'select[name="search[field]"]' ).val(), comparison: $table.find( 'select[name="search[comparison]"]' ).val(), term: $table.find( 'input[name="search[term]"]' ).val(), }, nonce: wpforms_admin.nonce, url: $url, }; // Trigger alert modal to confirm. $.confirm( { title: wpforms_admin.heads_up, content: filteredCount && $( '#wpforms-reset-filter' ).length ? $noticeData.content.replace( '{entry_count}', filteredCount ) : $noticeData.contentAll, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action: () => { $.get( wpforms_admin.ajax_url, data ) .done( function( response ) { if ( response.success ) { window.location = ! _.isEmpty( response.data ) ? response.data : $url; } } ); }, }, cancel: { text: wpforms_admin.cancel, keys: [ 'esc' ], }, }, } ); } ); // Check for new form entries using Heartbeat API. $( document ).on( 'heartbeat-send', function( event, data ) { var $entriesList = $( '#wpforms-entries-list' ); // Works on entry list page only. if ( ! $entriesList.length || $entriesList.find( '.wpforms-dash-widget' ).length ) { return; } var last_entry_id = $entriesList.find( '#wpforms-entries-table' ).data( 'last-entry-id' ); // When entries list is filtered, there is no data param at all. if ( typeof last_entry_id === 'undefined' ) { return; } data.wpforms_new_entries_entry_id = last_entry_id; data.wpforms_new_entries_form_id = $entriesList.find( 'input[name=form_id]' ).val(); } ); // Display entries list notification if Heartbeat API new form entries check is successful. $( document ).on( 'heartbeat-tick', function( event, data ) { var columnCount; var $entriesList = $( '#wpforms-entries-list' ); // Works on entry list page only. if ( ! $entriesList.length ) { return; } if ( ! data.wpforms_new_entries_notification ) { return; } columnCount = $entriesList.find( '.wp-list-table thead tr' ).first().children().length; if ( ! $entriesList.find( '.new-entries-notification' ).length ) { $entriesList.find( '.wp-list-table thead' ) .append( '' ); } var $link = $entriesList.find( '.new-entries-notification a' ); $link .text( data.wpforms_new_entries_notification ) .slideDown( { start: function() { $link.css( 'display', 'block' ); }, always: function() { $link.css( 'display', 'block' ); }, } ); } ); }, /** * Element bindings for Entries Single (Details) page. * * @since 1.3.9 */ initEntriesSingle: function() { // Entry navigation hotkeys. // We only want to listen on the applicable admin page. if ( 'wpforms-entries' === WPFormsAdmin.getQueryString( 'page' ) && 'details' === WPFormsAdmin.getQueryString( 'view' ) ) { WPFormsAdmin.entryHotkeys(); } // Confirm entry deletion. $( document ).on( 'click', '#wpforms-entries-single .wpforms-entry-delete a', function( event ) { event.preventDefault(); const url = $( this ).attr( 'href' ); // Trigger alert modal to confirm. $.confirm( { title: wpforms_admin.heads_up, content: wpforms_admin.entry_delete_confirm, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action: function() { window.location = url; }, }, cancel: { text: wpforms_admin.cancel, keys: [ 'esc' ], }, }, } ); } ); // Confirm entry trash. $( document ).on( 'click', '#wpforms-entries-single .trash', function( event ) { event.preventDefault(); const url = $( this ).attr( 'href' ); // Trigger alert modal to confirm. $.confirm( { title: wpforms_admin.heads_up, content: wpforms_admin.entry_trash_confirm, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action: () => { window.location = url; }, }, cancel: { text: wpforms_admin.cancel, keys: [ 'esc' ], }, }, } ); } ); // Open Print preview in new window. $( document ).on( 'click', '#wpforms-entries-single .wpforms-entry-print a', function( event ) { event.preventDefault(); window.open( $( this ).attr( 'href' ) ); } ); // Toggle displaying empty fields. $( document ).on( 'click', '#wpforms-entries-single .wpforms-empty-field-toggle', function( event ) { event.preventDefault(); // Handle cookie. if ( wpCookies.get( 'wpforms_entry_hide_empty' ) === 'true' ) { // User was hiding empty fields, so now display them. wpCookies.remove( 'wpforms_entry_hide_empty' ); $( this ).text( wpforms_admin.entry_empty_fields_hide ); } else { // User was seeing empty fields, so now hide them. wpCookies.set( 'wpforms_entry_hide_empty', 'true', 2592000 ); // 1month. $( this ).text( wpforms_admin.entry_empty_fields_show ); } $( '.wpforms-entry-field.empty, .wpforms-edit-entry-field.empty' ).toggle(); } ); // Display notes editor. $( document ).on( 'click', '#wpforms-entries-single .wpforms-entry-notes-new .add', function( event ) { event.preventDefault(); $( this ).hide().next( 'form' ).stop().slideToggle(); } ); // Cancel note. $( document ).on( 'click', '#wpforms-entries-single .wpforms-entry-notes-new .cancel', function( event ) { event.preventDefault(); $( this ).closest( 'form' ).stop().slideToggle(); $( '.wpforms-entry-notes-new .add' ).show(); } ); // Delete note. $( document ).on( 'click', '#wpforms-entries-single .wpforms-entry-notes-byline .note-delete', function( event ) { event.preventDefault(); var url = $( this ).attr( 'href' ); // Trigger alert modal to confirm. $.confirm( { title: wpforms_admin.heads_up, content: wpforms_admin.entry_note_delete_confirm, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action: function() { window.location = url; }, }, cancel: { text: wpforms_admin.cancel, keys: [ 'esc' ], }, }, } ); } ); }, //--------------------------------------------------------------------// // Entry List //--------------------------------------------------------------------// /** * Hotkeys for Entries Single (Details) page. * * j triggers previous entry, k triggers next entry. * * @since 1.4.0 */ entryHotkeys: function() { // eslint-disable-next-line complexity $( document ).on( 'keydown', function( event ) { if ( 74 === event.keyCode && ! event.metaKey && ! WPFormsAdmin.isFormTypeNode( event.target.nodeName ) ) { // j key has been pressed outside a form element, go to the previous entry. var prevEntry = $( '#wpforms-admin-single-navigation-prev-link' ).attr( 'href' ); if ( '#' !== prevEntry ) { window.location.href = prevEntry; } } else if ( 75 === event.keyCode && ! event.metaKey && ! WPFormsAdmin.isFormTypeNode( event.target.nodeName ) ) { // k key has been pressed outside a form element, go to the previous entry. var nextEntry = $( '#wpforms-admin-single-navigation-next-link' ).attr( 'href' ); if ( '#' !== nextEntry ) { window.location.href = nextEntry; } } } ); }, //--------------------------------------------------------------------// // Welcome Activation. //--------------------------------------------------------------------// /** * Welcome activation page. * * @since 1.3.9 */ initWelcome: function() { // Open modal and play How To video. $( document ).on( 'click', '#wpforms-welcome .play-video', function( event ) { event.preventDefault(); const video = '
    '; $.dialog( { title: false, content: video, closeIcon: true, boxWidth: '70%', } ); } ); }, //--------------------------------------------------------------------// // Addons List. //--------------------------------------------------------------------// /** * Element bindings for Addons List page. * * @since 1.3.9 */ initAddons: function() { // Only run on the addons page. if ( ! $( '#wpforms-admin-addons' ).length ) { return; } // Addons searching. const $sectionAll = $( '#wpforms-addons-list-section-all' ); const $sectionInstalled = $( '#wpforms-addons-list-section-installed' ); if ( $sectionAll.length || $sectionInstalled.length ) { let addonSearchInstalled; let addonSearchAll; if ( $sectionInstalled.length ) { addonSearchInstalled = new List( 'wpforms-addons-list-section-installed', { valueNames: [ 'addon-link' ], } ); } if ( $sectionAll.length ) { addonSearchAll = new List( 'wpforms-addons-list-section-all', { valueNames: [ 'addon-link' ], } ); } $( '#wpforms-addons-search' ).on( 'keyup search', function() { WPFormsAdmin.updateAddonSearchResult( this, addonSearchAll, addonSearchInstalled ); } ); } // Toggle an addon state. $( document ).on( 'change', '.wpforms-addons-list-item .wpforms-toggle-control input', function( event ) { event.preventDefault(); if ( $( this ).hasClass( 'disabled' ) ) { return false; } WPFormsAdmin.addonToggleNew( $( this ) ); } ); $( document ).on( 'click', '.wpforms-addons-list-item button', function( event ) { event.preventDefault(); if ( $( this ).hasClass( 'disabled' ) ) { return false; } WPFormsAdmin.addonToggleNew( $( this ) ); } ); $( document ).on( 'click', '#wpforms-admin-addons .addon-item button', function( event ) { event.preventDefault(); if ( $( this ).hasClass( 'disabled' ) ) { return false; } WPFormsAdmin.addonToggle( $( this ) ); } ); }, /** * Handle addons search field operations. * * @since 1.7.4 * * @param {Object} searchField The search field html element. * @param {Object} addonSearchAll Addons all list (uses List.js). * @param {Object} addonSearchInstalled Addons installed list (uses List.js). */ updateAddonSearchResult( searchField, addonSearchAll, addonSearchInstalled ) { let searchTerm = $( searchField ).val(); /* * Replace dot and comma with space * it is workaround for a bug in list.js library. * * Note: remove when the issue below is fixed: * @see https://github.com/javve/list.js/issues/699 */ searchTerm = searchTerm.replace( /[.,]/g, ' ' ); const $noResultsMessage = $( '#wpforms-addons-no-results' ); const $sectionAll = $( '#wpforms-addons-list-section-all' ); const $sectionInstalled = $( '#wpforms-addons-list-section-installed' ); const searchResultsAll = addonSearchAll ? addonSearchAll.search( searchTerm ) : []; const searchResultsInstalled = addonSearchInstalled ? addonSearchInstalled.search( searchTerm ) : []; $noResultsMessage.toggle( searchResultsAll.length === 0 && searchResultsInstalled.length === 0 ); $sectionAll.toggle( searchResultsAll.length > 0 ); $sectionInstalled.toggle( searchResultsInstalled.length > 0 ); }, /** * Change plugin/addon state. * * @since 1.6.3 * * @param {string} plugin Plugin slug or URL for download. * @param {string} state State status activate|deactivate|install. * @param {string} pluginType Plugin type addon or plugin. * @param {Function} callback Callback for get result from AJAX. * @param {Function} errorCallback Callback for get error from AJAX. */ setAddonState( plugin, state, pluginType, callback, errorCallback ) { const actions = { activate: 'wpforms_activate_addon', install: 'wpforms_install_addon', deactivate: 'wpforms_deactivate_addon', incompatible: 'wpforms_activate_addon', }; const action = actions[ state ]; if ( ! action ) { return; } const data = { action, nonce: wpforms_admin.nonce, plugin, type: pluginType, }; $.post( wpforms_admin.ajax_url, data, function( res ) { callback( res ); } ).fail( function( xhr ) { errorCallback( xhr ); } ); }, /** * Toggle addon state. * * @since 1.8.6 * * @param {Object} $btn Button element. */ // eslint-disable-next-line max-lines-per-function, complexity addonToggleNew( $btn ) { const $footer = $btn.parents( '.wpforms-addons-list-item-footer' ); const classes = { active: 'wpforms-addons-list-item-footer-active', activating: 'wpforms-addons-list-item-footer-activating', incompatible: 'wpforms-addons-list-item-footer-incompatible', installed: 'wpforms-addons-list-item-footer-installed', missing: 'wpforms-addons-list-item-footer-missing', goToUrl: 'wpforms-addons-list-item-footer-go-to-url', withError: 'wpforms-addons-list-item-footer-with-error', }; // Open url in new tab. if ( $footer.hasClass( classes.goToUrl ) ) { window.open( $btn.attr( 'data-plugin' ), '_blank' ); return; } $btn.prop( 'disabled', true ); let checked = $btn.is( ':checked' ); let cssClass; const plugin = $footer.attr( 'data-plugin' ); const pluginType = $footer.attr( 'data-type' ); const $addon = $btn.parents( '.wpforms-addons-list-item' ); const state = WPFormsAdmin.getAddonState( $footer, classes, $btn ); /** * Handle error. * * @param {Object} res Response object. */ function handleError( res ) { $footer.addClass( classes.withError ); if ( typeof res.data === 'object' ) { $footer.append( `` ); } else { $footer.append( `` ); } if ( state === 'install' ) { checked = false; WPFormsAdmin.removeSpinnerFromButton( $btn ); } else if ( state === 'deactivate' ) { checked = true; } else if ( state === 'activate' ) { checked = false; } } /** * Handle success. * * @param {Object} res Response object. */ function handleSuccess( res ) { if ( state === 'install' ) { cssClass = classes.active; checked = true; $footer.attr( 'data-plugin', res.data.basename ); if ( ! res.data.is_activated ) { cssClass = classes.installed; checked = false; } $btn.hide(); $btn = $btn.closest( '.wpforms-addons-list-item' ).find( '.wpforms-toggle-control input' ); } else if ( state === 'activate' ) { $footer.find( '.wpforms-addons-list-item-footer-settings-link' ).fadeIn( 150 ); cssClass = classes.active; checked = true; } else if ( state === 'deactivate' ) { $footer.find( '.wpforms-addons-list-item-footer-settings-link' ).fadeOut( 150 ); cssClass = classes.installed; checked = false; } $footer.removeClass( classes.active + ' ' + classes.incompatible + ' ' + classes.installed + ' ' + classes.missing ).addClass( cssClass ); } WPFormsAdmin.setAddonState( plugin, state, pluginType, function( res ) { if ( res.success ) { handleSuccess( res ); } else { handleError( res ); } WPFormsAdmin.updateAddonButtonPropertiesAndUI( $btn, $addon, $footer, classes, checked ); }, function() { handleError( { data: wpforms_admin.server_error, } ); WPFormsAdmin.updateAddonButtonPropertiesAndUI( $btn, $addon, $footer, classes, checked ); } ); }, /** * Add spinner to button. * * @since 1.8.6 * * @param {Object} $button Button element. */ addSpinnerToButton( $button ) { const spinnerBlue = ''; const originalWidth = $button.width(); $button.data( 'original-text', $button.html() ); $button.width( originalWidth ).html( spinnerBlue ); }, /** * Remove spinner from button. * * @since 1.8.6 * * @param {Object} $button Button element. */ removeSpinnerFromButton( $button ) { $button.html( $button.data( 'original-text' ) ); }, /** * Get addon state. * * @since 1.8.6 * * @param {Object} $footer Footer element. * @param {Object} classes Classes object. * @param {Object} $button Button element. * * @return {string} State. */ getAddonState( $footer, classes, $button ) { if ( $footer.hasClass( classes.active ) || $footer.hasClass( classes.incompatible ) ) { return 'deactivate'; } if ( $footer.hasClass( classes.installed ) ) { return 'activate'; } if ( $footer.hasClass( classes.missing ) ) { WPFormsAdmin.addSpinnerToButton( $button ); return 'install'; } return ''; }, /** * Update button properties and UI. * * @since 1.8.6 * * @param {Object} $btn Button element. * @param {Object} $addon Addon element. * @param {Object} $footer Footer element. * @param {Object} classes Classes object. * @param {boolean} checked Checked state. */ updateAddonButtonPropertiesAndUI( $btn, $addon, $footer, classes, checked ) { $btn.prop( 'checked', checked ); $btn.prop( 'disabled', false ); $btn.siblings( '.wpforms-toggle-control-status' ).html( $btn.siblings( '.wpforms-toggle-control-status' ).data( checked ? 'on' : 'off' ) ); if ( $addon.find( '.wpforms-addons-list-item-footer-error' ).length > 0 ) { setTimeout( function() { $footer.removeClass( classes.withError ); $addon.find( '.wpforms-addons-list-item-footer-error' ).remove(); }, 6000 ); } }, /** * Scroll to integration. * * @since 1.8.6 */ scrollToIntegration() { const currentURL = window.location.href; // eslint-disable-next-line compat/compat const urlObject = new URL( currentURL ); const searchParams = urlObject.searchParams; const addon = searchParams.get( 'addon' ); if ( addon ) { const $elementToScrollTo = $( '.wpforms-settings-provider[id*="' + addon + '"]' ); if ( $elementToScrollTo.length ) { $( window ).scrollTop( $elementToScrollTo.offset().top ); searchParams.delete( 'addon' ); window.history.pushState( {}, document.title, urlObject.toString() ); } } }, /** * Toggle addon state. * * @since 1.3.9 * * @param {Object} $btn Button element. */ // eslint-disable-next-line max-lines-per-function,complexity addonToggle( $btn ) { let state, cssClass, stateText, buttonText, errorText, successText; if ( $btn.hasClass( 'status-go-to-url' ) ) { // Open url in new tab. window.open( $btn.attr( 'data-plugin' ), '_blank' ); return; } $btn.prop( 'disabled', true ).addClass( 'loading' ); $btn.html( s.iconSpinner ); const pluginType = $btn.attr( 'data-type' ); if ( $btn.hasClass( 'status-active' ) ) { // Deactivate. state = 'deactivate'; cssClass = 'status-installed'; if ( pluginType === 'plugin' ) { cssClass += ' button button-secondary'; } stateText = wpforms_admin.addon_inactive; buttonText = wpforms_admin.addon_activate; errorText = wpforms_admin.addon_deactivate; if ( pluginType === 'addon' ) { buttonText = s.iconActivate + buttonText; errorText = s.iconDeactivate + errorText; } } else if ( $btn.hasClass( 'status-installed' ) ) { // Activate. state = 'activate'; cssClass = 'status-active'; if ( pluginType === 'plugin' ) { cssClass += ' button button-secondary disabled'; } stateText = wpforms_admin.addon_active; buttonText = wpforms_admin.addon_deactivate; if ( pluginType === 'addon' ) { buttonText = s.iconDeactivate + buttonText; errorText = s.iconActivate + wpforms_admin.addon_activate; } else if ( pluginType === 'plugin' ) { buttonText = wpforms_admin.addon_activated; errorText = wpforms_admin.addon_activate; } } else if ( $btn.hasClass( 'status-missing' ) ) { // Install & Activate. state = 'install'; cssClass = 'status-active'; if ( pluginType === 'plugin' ) { cssClass += ' button disabled'; } stateText = wpforms_admin.addon_active; buttonText = wpforms_admin.addon_activated; errorText = s.iconInstall; if ( pluginType === 'addon' ) { buttonText = s.iconActivate + wpforms_admin.addon_deactivate; errorText += wpforms_admin.addon_install; } } else { return; } const plugin = $btn.attr( 'data-plugin' ); // eslint-disable-next-line complexity WPFormsAdmin.setAddonState( plugin, state, pluginType, function( res ) { const $addon = $btn.closest( '.addon-item' ); if ( res.success ) { if ( 'install' === state ) { $btn.attr( 'data-plugin', res.data.basename ); successText = res.data.msg; if ( ! res.data.is_activated ) { stateText = wpforms_admin.addon_inactive; buttonText = 'plugin' === pluginType ? wpforms_admin.addon_activate : s.iconActivate + wpforms_admin.addon_activate; cssClass = 'plugin' === pluginType ? 'status-installed button button-secondary' : 'status-installed'; } } else { successText = res.data; } $addon.find( '.actions' ).append( '
    ' + successText + '
    ' ); $addon.find( 'span.status-label' ) .removeClass( 'status-active status-installed status-missing' ) .addClass( cssClass ) .removeClass( 'button button-primary button-secondary disabled' ) .text( stateText ); $btn .removeClass( 'status-active status-installed status-missing' ) .removeClass( 'button button-primary button-secondary disabled' ) .addClass( cssClass ).html( buttonText ); } else { if ( 'object' === typeof res.data ) { if ( pluginType === 'addon' ) { $addon.find( '.actions' ).append( '

    ' + wpforms_admin.addon_error + '

    ' ); } else { $addon.find( '.actions' ).append( '

    ' + wpforms_admin.plugin_error + '

    ' ); } } else { $addon.find( '.actions' ).append( '

    ' + res.data + '

    ' ); } if ( 'install' === state && 'plugin' === pluginType ) { $btn.addClass( 'status-go-to-url' ).removeClass( 'status-missing' ); } $btn.html( errorText ); } $btn.prop( 'disabled', false ).removeClass( 'loading' ); if ( ! $addon.find( '.actions' ).find( '.msg.error' ).length ) { setTimeout( function() { $( '.addon-item .msg' ).remove(); }, 3000 ); } }, function( error ) { // eslint-disable-next-line no-console console.log( error.responseText ); } ); }, //--------------------------------------------------------------------// // Settings. //--------------------------------------------------------------------// /** * Element bindings for Settings page. * * @since 1.3.9 */ initSettings: function() { // On ready events. $( document ).on( 'wpformsReady', function() { // Only proceed if we're on the settings page. if ( ! $( '#wpforms-settings' ).length ) { return; } // Watch for hashes and scroll to if found. // Display all addon boxes as the same height. var integrationFocus = WPFormsAdmin.getQueryString( 'wpforms-integration' ), jumpTo = WPFormsAdmin.getQueryString( 'jump' ); if ( integrationFocus ) { $( 'body' ).animate( { scrollTop: $( '#wpforms-integration-' + integrationFocus ).offset().top }, 1000 ); } else if ( jumpTo ) { $( 'body' ).animate( { scrollTop: $( '#' + jumpTo ).offset().top }, 1000 ); } // Settings conditional logic. $( '.wpforms-admin-settings-form' ).conditions( [ // Misc > Disable User Cookies visibility. { conditions: { element: '#wpforms-setting-gdpr', type: 'checked', operator: 'is', }, actions: { if: { element: '#wpforms-setting-row-gdpr-disable-uuid,#wpforms-setting-row-gdpr-disable-details', action: 'show', }, else : { element: '#wpforms-setting-row-gdpr-disable-uuid,#wpforms-setting-row-gdpr-disable-details', action: 'hide', }, }, effect: 'appear', }, // CAPTCHA > Type. { conditions: { element: 'input[name=captcha-provider]:checked', type: 'value', operator: '=', condition: 'hcaptcha', }, actions: { if: [ { element: '.wpforms-setting-row', action: 'show', }, { element: '.wpforms-setting-recaptcha, #wpforms-setting-row-recaptcha-site-key, #wpforms-setting-row-recaptcha-secret-key, #wpforms-setting-row-recaptcha-fail-msg, .wpforms-setting-turnstile, #wpforms-setting-row-turnstile-heading, #wpforms-setting-row-turnstile-site-key, #wpforms-setting-row-turnstile-secret-key, #wpforms-setting-row-turnstile-theme, #wpforms-setting-row-turnstile-fail-msg', action: 'hide', }, ], }, effect: 'appear', }, { conditions: { element: 'input[name=captcha-provider]:checked', type: 'value', operator: '=', condition: 'recaptcha', }, actions: { if: [ { element: '.wpforms-setting-row', action: 'show', }, { element: '#wpforms-setting-row-hcaptcha-heading, #wpforms-setting-row-hcaptcha-site-key, #wpforms-setting-row-hcaptcha-secret-key, #wpforms-setting-row-hcaptcha-fail-msg, #wpforms-setting-row-turnstile-heading, #wpforms-setting-row-turnstile-site-key, #wpforms-setting-row-turnstile-secret-key, #wpforms-setting-row-turnstile-theme, #wpforms-setting-row-turnstile-fail-msg', action: 'hide', }, ], }, effect: 'appear', }, { conditions: { element: 'input[name=captcha-provider]:checked', type: 'value', operator: '=', condition: 'turnstile', }, actions: { if: [ { element: '.wpforms-setting-row', action: 'show', }, { element: '#wpforms-setting-row-hcaptcha-heading, #wpforms-setting-row-hcaptcha-site-key, #wpforms-setting-row-hcaptcha-secret-key, #wpforms-setting-row-hcaptcha-fail-msg, .wpforms-setting-recaptcha, #wpforms-setting-row-recaptcha-site-key, #wpforms-setting-row-recaptcha-secret-key, #wpforms-setting-row-recaptcha-fail-msg', action: 'hide', }, ], }, effect: 'appear', }, { conditions: { element: 'input[name=captcha-provider]:checked', type: 'value', operator: '=', condition: 'none', }, actions: { if: [ { element: '.wpforms-setting-row', action: 'hide', }, { element: '.wpforms-setting-captcha-heading, #wpforms-setting-row-captcha-provider', action: 'show', }, ], }, effect: 'appear', }, ] ); } ); // Render engine setting. $( document ).on( 'change', '#wpforms-setting-row-render-engine input', WPFormsAdmin.settingsRenderEngineChange ); // Form styles plugin setting. $( document ).on( 'change', '#wpforms-setting-disable-css', function() { WPFormsAdmin.settingsFormStylesAlert( $( this ).val() ); } ); // Image upload fields. $( document ).on( 'click', '.wpforms-setting-row-image button', function( event ) { event.preventDefault(); // If the remove button was clicked, clear the value and remove the image. if ( $( this ).hasClass( 'wpforms-setting-remove-image' ) ) { const $wrapper = $( this ).closest( '.wpforms-setting-row-image' ); $wrapper.find( 'input' ).val( '' ).attr( 'value', '' ).trigger( 'change' ).end().find( 'img' ).remove(); return; } WPFormsAdmin.imageUploadModal( $( this ) ); } ); // Verify license key. $( document ).on( 'click', '#wpforms-setting-license-key-verify', function( event ) { event.preventDefault(); WPFormsAdmin.licenseVerify( $( this ) ); } ); // Show message for license field. $( document ).on( 'click', '.wpforms-setting-license-wrapper', function( event ) { event.preventDefault(); var $keyField = $( '#wpforms-setting-license-key' ); if ( ! $keyField.length ) { return; } if ( ! $keyField.prop( 'disabled' ) ) { return; } WPFormsAdmin.licenseEditMessage(); } ); // Deactivate license key. $( document ).on( 'click', '#wpforms-setting-license-key-deactivate', function( event ) { event.preventDefault(); WPFormsAdmin.licenseDeactivate( $( this ) ); } ); // Refresh license key. $( document ).on( 'click', '#wpforms-setting-license-key-refresh', function( event ) { event.preventDefault(); WPFormsAdmin.licenseRefresh( $( this ) ); } ); /** * @todo Refactor providers settings tab. Code below is legacy. */ // Integration connect. $( document ).on( 'click', '.wpforms-settings-provider-connect', function( event ) { event.preventDefault(); var button = $( this ); WPFormsAdmin.integrationConnect( button ); } ); // Integration account disconnect. $( document ).on( 'click', '.wpforms-settings-provider-accounts-list .remove a', function( event ) { event.preventDefault(); WPFormsAdmin.integrationDisconnect( $( this ) ); } ); // Integration individual display toggling. $( document ).on( 'click', '.wpforms-settings-provider:not(.focus-out) .wpforms-settings-provider-header', function( event ) { event.preventDefault(); var $this = $( this ); $this .parent() .find( '.wpforms-settings-provider-accounts' ) .stop( false, true ) .slideToggle( '', function() { $this.parent().find( '.wpforms-settings-provider-logo i' ).toggleClass( 'fa-chevron-right fa-chevron-down' ); } ); } ); // Integration accounts display toggling. $( document ).on( 'click', '.wpforms-settings-provider-accounts-toggle a', function( event ) { event.preventDefault(); var $connectFields = $( this ).parent().next( '.wpforms-settings-provider-accounts-connect' ); $connectFields.find( 'input[type=text], input[type=password]' ).val( '' ); $connectFields.stop().slideToggle(); } ); // CAPTCHA settings page: type toggling. $( document ).on( 'change', '#wpforms-setting-row-captcha-provider input', function() { var $preview = $( '#wpforms-setting-row-captcha-preview' ); if ( this.value === 'hcaptcha' || this.value === 'turnstile' ) { $preview.removeClass( 'wpforms-hidden' ); } else if ( this.value === 'none' ) { $preview.addClass( 'wpforms-hidden' ); } else { $( '#wpforms-setting-row-recaptcha-type input:checked' ).trigger( 'change' ); } if ( $preview.find( '.wpforms-captcha-preview' ).length ) { $preview.find( '.wpforms-captcha-preview' ).empty(); $preview.find( '.wpforms-captcha-placeholder' ).removeClass( 'wpforms-hidden' ); } } ); // CAPTCHA settings page: reCAPTCHA type toggling. $( document ).on( 'change', '#wpforms-setting-row-recaptcha-type input', function() { $( '#wpforms-setting-row-captcha-preview' ).toggleClass( 'wpforms-hidden', 'v2' !== this.value ); $( '#wpforms-setting-row-recaptcha-v3-threshold' ).toggleClass( 'wpforms-hidden', 'v3' !== this.value ); } ); // Toggle control switch description. $( document ).on( 'change', '.wpforms-toggle-control input', function() { const $input = $( this ), checked = $input.is( ':checked' ), state = checked ? 'on' : 'off', $field = $input.closest( '.wpforms-setting-field' ), $control = $input.closest( '.wpforms-toggle-control' ), $status = $control.find( '.wpforms-toggle-control-status' ), $descOn = $field.find( '.wpforms-toggle-desc.desc-on' ), $descOff = $field.find( '.wpforms-toggle-desc.desc-off' ), isDoubleDesc = $descOn.length > 0 && $descOff.length > 0; $descOn.toggleClass( 'wpforms-hidden', ! checked && isDoubleDesc ); $descOff.toggleClass( 'wpforms-hidden', checked && isDoubleDesc ); $status.html( $status.data( state ) ); } ); }, /** * Render engine setting change event handler. * * @since 1.8.1 * * @param {object} e Event object. */ settingsRenderEngineChange: function( e ) { // noinspection JSUnusedLocalSymbols // eslint-disable-next-line const renderEngine = $( this ).val(); // TODO: Add corresponding code that need to be executed on change render engine setting. }, /** * Alert users if they change form styles to something that may give * unexpected results. * * @since 1.5.0 */ settingsFormStylesAlert: function( value ) { if ( '2' === value ) { var msg = wpforms_admin.settings_form_style_base; } else if ( '3' === value ) { var msg = wpforms_admin.settings_form_style_none; } else { return; } $.alert( { title: wpforms_admin.heads_up, content: msg, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); }, /** * Image upload modal window. * * @since 1.3.0 * * @param {jQuery} $el Image upload button element. */ imageUploadModal( $el ) { // To prevent caching of the media frame object and // avoid confusion between multiple instances, // this method no longer relies on the shared s.mediaFrame object. // Instead, it creates a new mediaFrame object for each instance. const $setting = $el.closest( '.wpforms-setting-field' ); s.mediaFrame = wpf.initMediaLibrary( { title: wpforms_admin.upload_image_title, extensions: wpforms_admin.upload_image_extensions, extensionsError: wpforms_admin.upload_image_extensions_error, buttonText: wpforms_admin.upload_image_button, } ); s.mediaFrame.on( 'select', function() { // Grab our attachment selection and construct a JSON representation of the model. const mediaAttachment = s.mediaFrame.state().get( 'selection' ).first().toJSON(); const $input = $setting.find( 'input[type=text]' ); // Send the attachment URL to our custom input field via jQuery. $input.val( mediaAttachment.url ); $setting.find( 'img' ).remove(); $setting.prepend( '' ); $input.trigger( 'change' ); } ).on( 'close', function() { s.mediaFrame.off( 'library:selection:add' ); } ); // Now that everything has been set, let's open up the frame. s.mediaFrame.open(); }, /** * Verify a license key. * * @since 1.3.9 * * @param {jQuery} $el Verify button element. */ licenseVerify: function( $el ) { var $row = $el.closest( '.wpforms-setting-row' ), $keyField = $( '#wpforms-setting-license-key' ), buttonWidth = $el.outerWidth(), buttonLabel = $el.text(), data = { action: 'wpforms_verify_license', nonce: wpforms_admin.nonce, license: $keyField.val(), }; $el.html( s.iconSpinner ).css( 'width', buttonWidth ).prop( 'disabled', true ); $.post( wpforms_admin.ajax_url, data, function( res ) { var icon = 'fa fa-check-circle', color = 'green', msg; if ( res.success ) { msg = res.data.msg; $el.hide(); $row.find( '#wpforms-setting-license-key-info-message' ).empty().hide(); $row.find( '.type, .desc, #wpforms-setting-license-key-deactivate' ).show(); $row.find( '.type strong' ).text( res.data.type ); $( '.wpforms-license-notice' ).remove(); $keyField .prop( 'disabled', true ) .addClass( 'wpforms-setting-license-is-valid' ) .attr( 'value', $keyField.val() ); } else { icon = 'fa fa-exclamation-circle'; color = 'orange'; msg = res.data; $row.find( '.type, .desc, #wpforms-setting-license-key-deactivate' ).hide(); $keyField.prop( 'disabled', false ); } $.alert( { title: msg.header ?? false, content: msg.msg ?? msg, icon: icon, type: color, buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); $el.html( buttonLabel ).css( 'width', 'auto' ).prop( 'disabled', false ); } ).fail( function( xhr ) { $keyField.prop( 'disabled', false ); console.log( xhr.responseText ); } ); }, /** * Show message that license key editing is disabled. * * @since 1.6.5 */ licenseEditMessage: function() { $.alert( { title: wpforms_admin.heads_up, content: wpforms_admin.edit_license, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); }, /** * Deactivate a license key. * * @since 1.3.9 * * @param {Element} el Button element. */ licenseDeactivate: function( el ) { const $this = $( el ); const $row = $this.closest( '.wpforms-setting-row' ); const buttonWidth = $this.outerWidth(); const buttonLabel = $this.text(); const data = { action: 'wpforms_deactivate_license', nonce: wpforms_admin.nonce, }; $this.html( s.iconSpinner ).css( 'width', buttonWidth ).prop( 'disabled', true ); $.post( wpforms_admin.ajax_url, data, function( res ) { let icon = 'fa fa-info-circle'; let color = 'blue'; let title = wpforms_admin.success; const data = res.data; const msg = ! data.msg || typeof data.msg !== 'string' ? wpforms_admin.something_went_wrong : data.msg; if ( res.success ) { $row.find( '#wpforms-setting-license-key' ) .val( '' ) .attr( 'value', '' ) .prop( { readonly: false, disabled: false } ) .removeClass(); $row.find( '.wpforms-license-key-deactivate-remove' ).remove(); $row.find( '#wpforms-setting-license-key-info-message' ).html( data.info ).show(); $row.find( '#wpforms-setting-license-key-verify' ).prop( 'disabled', false ).show(); $row.find( '.type, .desc, #wpforms-setting-license-key-deactivate' ).hide(); } else { icon = 'fa fa-exclamation-circle'; color = 'orange'; title = wpforms_admin.oops; } $.alert( { title: title, content: msg, icon: icon, type: color, buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); $this.html( buttonLabel ).css( 'width', 'auto' ).prop( 'disabled', false ); } ).fail( function( xhr ) { console.log( xhr.responseText ); } ); }, /** * Refresh a license key. * * @since 1.3.9 */ licenseRefresh: function( el ) { var $this = $( el ), $row = $this.closest( '.wpforms-setting-row' ), $input = $( '#wpforms-setting-license-key' ), data = { action: 'wpforms_refresh_license', nonce: wpforms_admin.nonce, license: $input.val(), }; $.post( wpforms_admin.ajax_url, data, function( res ) { var icon = 'fa fa-check-circle', color = 'green', msg; if ( res.success ) { msg = res.data.msg; $row.find( '.type strong' ).text( res.data.type ); } else { icon = 'fa fa-exclamation-circle'; color = 'orange'; msg = res.data; $row.find( '.type, .desc' ).hide(); $input.removeClass( 'wpforms-setting-license-is-valid' ).addClass( 'wpforms-setting-license-is-invalid' ); } $.alert( { title: msg.header ?? false, content: msg.msg ?? msg, icon: icon, type: color, buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); } ).fail( function( xhr ) { console.log( xhr.responseText ); } ); }, /** * Connect integration provider account. * * @param $btn Button (.wpforms-settings-provider-connect) that was clicked to establish connection. * * @since 1.3.9 */ integrationConnect: function( $btn ) { var buttonWidth = $btn.outerWidth(), buttonLabel = $btn.text(), $provider = $btn.closest( '.wpforms-settings-provider' ), data = { action : 'wpforms_settings_provider_add_' + $btn.data( 'provider' ), data : $btn.closest( 'form' ).serialize(), provider: $btn.data( 'provider' ), nonce : wpforms_admin.nonce, }, errorMessage = wpforms_admin.provider_auth_error; $btn.html( wpforms_admin.connecting ).css( 'width', buttonWidth ).prop( 'disabled', true ); $.post( wpforms_admin.ajax_url, data, function( response ) { if ( response.success ) { $provider.find( '.wpforms-settings-provider-accounts-list ul' ).append( response.data.html ); $provider.addClass( 'connected' ); $btn.closest( '.wpforms-settings-provider-accounts-connect' ).stop().slideToggle(); } else { if ( Object.prototype.hasOwnProperty.call( response, 'data' ) && Object.prototype.hasOwnProperty.call( response.data, 'error_msg' ) ) { errorMessage += '
    ' + response.data.error_msg; } WPFormsAdmin.integrationError( errorMessage ); } } ).fail( function() { WPFormsAdmin.integrationError( errorMessage ); } ).always( function() { $btn.html( buttonLabel ).css( 'width', 'auto' ).prop( 'disabled', false ); } ); }, /** * Remove integration provider account. * * @since 1.3.9 * * @param {object} el Disconnect link that was clicked to establish removing account. */ integrationDisconnect: function( el ) { var $this = $( el ), $provider = $this.parents( '.wpforms-settings-provider' ), data = { action : 'wpforms_settings_provider_disconnect_' + $this.data( 'provider' ), provider: $this.data( 'provider' ), key : $this.data( 'key' ), nonce : wpforms_admin.nonce, }, errorMessage = wpforms_admin.provider_delete_error; $.confirm( { title: wpforms_admin.heads_up, content: wpforms_admin.provider_delete_confirm, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action: function() { $.post( wpforms_admin.ajax_url, data, function( response ) { if ( response.success ) { $this.parent().parent().remove(); // Hide Connected status label if no more integrations are linked. var numberOfIntegrations = $provider.find( '.wpforms-settings-provider-accounts-list li' ).length; if ( typeof numberOfIntegrations === 'undefined' || numberOfIntegrations === 0 ) { $provider.removeClass( 'connected' ); } /** * Provider account has been removed. * * @since 1.7.7 */ $( document ).trigger( 'wpformsProviderRemoved', [ $provider, response ] ); } else { if ( Object.prototype.hasOwnProperty.call( response, 'data' ) && Object.prototype.hasOwnProperty.call( response.data, 'error_msg' ) ) { errorMessage += '
    ' + response.data.error_msg; } WPFormsAdmin.integrationError( errorMessage ); } } ).fail( function() { WPFormsAdmin.integrationError( errorMessage ); } ); }, }, cancel: { text: wpforms_admin.cancel, keys: [ 'esc' ], }, }, } ); }, /** * Error handling. * * @since 1.6.4 * * @param {string} error Error message. */ integrationError: function( error ) { $.alert( { title: wpforms_admin.something_went_wrong, content: error, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); }, //--------------------------------------------------------------------// // Tools. //--------------------------------------------------------------------// /** * Element bindings for Tools page. * * @since 1.4.2 */ initTools() { // Enable import/export buttons when a value is selected. $( document ).on( 'change', '#wpforms-tools-form-import, #wpforms-tools-form-other-import, #wpforms-tools-form-export, #wpforms-tools-form-template', function() { const $field = $( this ); const $button = $field.parents( 'form' ).find( 'button' ); $button.attr( 'aria-disabled', $field.val().length === 0 ); } ); // Copy system information to clipboard. $( document ).on( 'click', '#wpforms-system-information-copy', function( event ) { event.preventDefault(); WPFormsAdmin.copySystemInformation(); } ); // Run SSL test. $( document ).on( 'click', '#wpforms-ssl-verify', function( event ) { event.preventDefault(); WPFormsAdmin.verifySSLConnection(); } ); // Recreate database tables. $( document ).on( 'click', '#wpforms-recreate-tables', function( event ) { event.preventDefault(); WPFormsAdmin.recreateTables(); } ); // Run import for a specific provider. $( document ).on( 'click', '#wpforms-importer-forms-submit', function( event ) { event.preventDefault(); // Check to confirm user as selected a form. if ( $( '#wpforms-importer-forms input:checked' ).length ) { const ids = []; $( '#wpforms-importer-forms input:checked' ).each( function( i ) { ids[ i ] = $( this ).val(); } ); if ( ! wpforms_admin.isPro ) { // We need to analyze the forms before starting the actual import. WPFormsAdmin.analyzeForms( ids ); } else { // Begin the import process. WPFormsAdmin.importForms( ids ); } } else { // User didn't actually select a form so alert them. $.alert( { title: wpforms_admin.heads_up, content: wpforms_admin.importer_forms_required, icon: 'fa fa-info-circle', type: 'blue', buttons: { confirm: { text: wpforms_admin.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); } } ); // Continue import after analyzing. $( document ).on( 'click', '#wpforms-importer-continue-submit', function( event ) { event.preventDefault(); WPFormsAdmin.importForms( s.formIDs ); } ); }, /** * Copy system information to clipboard. * * @since 1.8.4 */ copySystemInformation() { $( '#wpforms-system-information' ).select(); document.execCommand( 'copy' ); }, /** * Perform test connection to verify that the current web host * can successfully make outbound SSL connections. * * @since 1.4.5 */ verifySSLConnection() { const $btn = $( '#wpforms-ssl-verify' ); const btnLabel = $btn.text(); const btnWidth = $btn.outerWidth(); const $settings = $btn.parent(); $btn.css( 'width', btnWidth ).prop( 'disabled', true ).text( wpforms_admin.testing ); const data = { action: 'wpforms_verify_ssl', nonce: wpforms_admin.nonce, }; // Trigger AJAX to test connection $.post( wpforms_admin.ajax_url, data, function( res ) { WPFormsAdmin.debug( res ); // Remove any previous alerts. $settings.find( '.wpforms-notice' ).remove(); if ( res.success ) { $btn.before( '
    ' + res.data.msg + '
    ' ); } if ( ! res.success && res.data.msg ) { $btn.before( '
    ' + res.data.msg + '
    ' ); } if ( ! res.success && res.data.debug ) { $btn.before( '
    ' + res.data.debug + '
    ' ); } $btn.css( 'width', btnWidth ).prop( 'disabled', false ).text( btnLabel ); } ); }, /** * Recreate custom tables. * * @since 1.9.0 */ recreateTables() { const $btn = $( '#wpforms-recreate-tables' ); const btnLabel = $btn.text(); const btnWidth = $btn.outerWidth(); const $settings = $btn.parent(); $btn.css( 'width', btnWidth ).prop( 'disabled', true ).text( wpforms_admin.recreating ); const data = { action: 'wpforms_recreate_tables', nonce: wpforms_admin.nonce, }; // Trigger AJAX to recreate tables. $.post( wpforms_admin.ajax_url, data, function( res ) { WPFormsAdmin.debug( res ); // Remove any previous alerts. $settings.find( '.wpforms-notice' ).remove(); if ( res.success ) { $btn.before( '
    ' + res.data.msg + '
    ' ); $btn.hide(); } if ( ! res.success && res.data.msg ) { $btn.before( '
    ' + res.data.msg + '
    ' ); } if ( ! res.success && res.data.debug ) { $btn.before( '
    ' + res.data.debug + '
    ' ); } } ).always( function() { $btn.css( 'width', btnWidth ).prop( 'disabled', false ).text( btnLabel ); } ); }, /** * Begins the process of analyzing the forms. * * This runs for non-Pro installs to check if any of the forms to be * imported contain fields * not currently available. * * @since 1.4.2 */ analyzeForms: function( forms ) { var $processAnalyze = $( '#wpforms-importer-analyze' ); // Display total number of forms we have to import. $processAnalyze.find( '.form-total' ).text( forms.length ); $processAnalyze.find( '.form-current' ).text( '1' ); // Hide the form select section. $( '#wpforms-importer-forms' ).hide(); // Show Analyze status. $processAnalyze.show(); // Create global analyze queue. s.analyzeQueue = forms; s.analyzed = 0; s.analyzeUpgrade = []; s.formIDs = forms; // Analyze the first form in the queue. WPFormsAdmin.analyzeForm(); }, /** * Analyze a single form from the queue. * * @since 1.4.2 */ analyzeForm: function() { var $analyzeSettings = $( '#wpforms-importer-analyze' ), formID = _.first( s.analyzeQueue ), provider = WPFormsAdmin.getQueryString( 'provider' ), data = { action: 'wpforms_import_form_' + provider, analyze: 1, form_id: formID, nonce: wpforms_admin.nonce, }; // Trigger AJAX analyze for this form. $.post( wpforms_admin.ajax_url, data, function( res ) { if ( res.success ) { if ( ! _.isEmpty( res.data.upgrade_plain ) || ! _.isEmpty( res.data.upgrade_omit ) ) { s.analyzeUpgrade.push( { name: res.data.name, fields: _.union( res.data.upgrade_omit, res.data.upgrade_plain ), } ); } // Remove this form ID from the queue. s.analyzeQueue = _.without( s.analyzeQueue, formID ); s.analyzed++; if ( _.isEmpty( s.analyzeQueue ) ) { if ( _.isEmpty( s.analyzeUpgrade ) ) { // Continue to import forms as no Pro fields were found. WPFormsAdmin.importForms( s.formIDs ); } else { // We found Pro fields, so alert the user. var upgradeDetails = wp.template( 'wpforms-importer-upgrade' ); $analyzeSettings.find( '.upgrade' ).append( upgradeDetails( s.analyzeUpgrade ) ); $analyzeSettings.find( '.upgrade' ).show(); $analyzeSettings.find( '.process-analyze' ).hide(); } } else { // Analyze next form in the queue. $analyzeSettings.find( '.form-current' ).text( s.analyzed + 1 ); WPFormsAdmin.analyzeForm(); } } } ); }, /** * Begins the process of importing the forms. * * @since 1.4.2 */ importForms: function( forms ) { var $processSettings = $( '#wpforms-importer-process' ); // Display total number of forms we have to import. $processSettings.find( '.form-total' ).text( forms.length ); $processSettings.find( '.form-current' ).text( '1' ); // Hide the form select and form analyze sections. $( '#wpforms-importer-forms, #wpforms-importer-analyze' ).hide(); // Show processing status. $processSettings.show(); // Create global import queue. s.importQueue = forms; s.imported = 0; // Import the first form in the queue. WPFormsAdmin.importForm(); }, /** * Imports a single form from the import queue. * * @since 1.4.2 */ importForm: function() { var $processSettings = $( '#wpforms-importer-process' ), formID = _.first( s.importQueue ), provider = WPFormsAdmin.getQueryString( 'provider' ), data = { action: 'wpforms_import_form_' + provider, form_id: formID, nonce: wpforms_admin.nonce, }; // Trigger AJAX import for this form. $.post( wpforms_admin.ajax_url, data, function( res ) { if ( res.success ) { var statusUpdate; if ( res.data.error ) { statusUpdate = wp.template( 'wpforms-importer-status-error' ); } else { statusUpdate = wp.template( 'wpforms-importer-status-update' ); } $processSettings.find( '.status' ).prepend( statusUpdate( res.data ) ); $processSettings.find( '.status' ).show(); // Remove this form ID from the queue. s.importQueue = _.without( s.importQueue, formID ); s.imported++; if ( _.isEmpty( s.importQueue ) ) { $processSettings.find( '.process-count' ).hide(); $processSettings.find( '.forms-completed' ).text( s.imported ); $processSettings.find( '.process-completed' ).show(); } else { // Import next form in the queue. $processSettings.find( '.form-current' ).text( s.imported + 1 ); WPFormsAdmin.importForm(); } } } ); }, //--------------------------------------------------------------------// // Upgrades (Tabs view). //--------------------------------------------------------------------// /** * Element bindings for Tools page. * * @since 1.4.3 */ initUpgrades: function() { // Prepare to run the v1.4.3 upgrade routine. $( document ).on( 'click', '#wpforms-upgrade-143 button', function( event ) { event.preventDefault(); var $this = $( this ), buttonWidth = $this.outerWidth(), $status = $( '#wpforms-upgrade-143 .status' ), data = { action: 'wpforms_upgrade_143', nonce: wpforms_admin.nonce, init: true, incomplete: $this.data( 'incomplete' ), }; // Change the button to indicate we are doing initial processing. $this.html( s.iconSpinner ).css( 'width', buttonWidth ).prop( 'disabled', true ); // Get the total number of entries, then kick off the routine. $.post( wpforms_admin.ajax_url, data, function( res ) { if ( res.success ) { // Set initial values. s.upgraded = Number( res.data.upgraded ); s.upgradeTotal = Number( res.data.total ); var percent = Math.round( ( Number( s.upgraded ) / Number( s.upgradeTotal ) ) * 100 ); // Show the status area. $this.remove(); $status.find( '.bar' ).css( 'width', percent + '%' ); $status.show().find( '.total' ).text( s.upgradeTotal ); $status.find( '.current' ).text( s.upgraded ); $status.find( '.percent' ).text( percent + '%' ); // Begin the actual upgrade routine. WPFormsAdmin.upgrade143(); } } ); } ); }, /** * The v1.4.3 entry fields upgrade routine. * * @since 1.4.3 */ upgrade143: function() { var $status = $( '#wpforms-upgrade-143 .status' ), data = { action: 'wpforms_upgrade_143', nonce: wpforms_admin.nonce, upgraded: s.upgraded, }; // Get the total number of entries, then kick off the routine. $.post( wpforms_admin.ajax_url, data, function( res ) { if ( res.success ) { s.upgraded = Number( s.upgraded ) + Number( res.data.count ); var percent = Math.round( ( Number( s.upgraded ) / Number( s.upgradeTotal ) ) * 100 ); // Update progress bar. $status.find( '.bar' ).css( 'width', percent + '%' ); if ( Number( res.data.count ) < 10 ) { // This batch completed the upgrade routine. $status.find( '.progress-bar' ).addClass( 'complete' ); $status.find( '.msg' ).text( wpforms_admin.upgrade_completed ); } else { $status.find( '.current' ).text( s.upgraded ); $status.find( '.percent' ).text( percent + '%' ); // Batch the next round of entries. WPFormsAdmin.upgrade143(); } } } ); }, /** * Element bindings for Flyout Menu. * * @since 1.5.7 */ initFlyoutMenu: function() { // Flyout Menu Elements. var $flyoutMenu = $( '#wpforms-flyout' ); if ( $flyoutMenu.length === 0 ) { return; } var $head = $flyoutMenu.find( '.wpforms-flyout-head' ), $sullie = $head.find( 'img' ), menu = { state: 'inactive', srcInactive: $sullie.attr( 'src' ), srcActive: $sullie.data( 'active' ), }; // Click on the menu head icon. $head.on( 'click', function( e ) { e.preventDefault(); if ( menu.state === 'active' ) { $flyoutMenu.removeClass( 'opened' ); $sullie.attr( 'src', menu.srcInactive ); menu.state = 'inactive'; } else { $flyoutMenu.addClass( 'opened' ); $sullie.attr( 'src', menu.srcActive ); menu.state = 'active'; } } ); // Page elements and other values. var $wpfooter = $( '#wpfooter' ); if ( $wpfooter.length === 0 ) { return; } var $overlap = $( '#wpforms-overview, ' + '#wpforms-entries-list, ' + '#wpforms-tools.wpforms-tools-tab-action-scheduler, ' + '#wpforms-tools.wpforms-tools-tab-logs' ); // Hide menu if scrolled down to the bottom of the page. $( window ).on( 'resize scroll', _.debounce( function( e ) { var wpfooterTop = $wpfooter.offset().top, wpfooterBottom = wpfooterTop + $wpfooter.height(), overlapBottom = $overlap.length > 0 ? $overlap.offset().top + $overlap.height() + 85 : 0, viewTop = $( window ).scrollTop(), viewBottom = viewTop + $( window ).height(); if ( wpfooterBottom <= viewBottom && wpfooterTop >= viewTop && overlapBottom > viewBottom ) { $flyoutMenu.addClass( 'out' ); } else { $flyoutMenu.removeClass( 'out' ); } }, 50 ) ); $( window ).trigger( 'scroll' ); }, /** * Lity improvements. * * @since 1.5.8 */ initLity: function() { // Use `data-lity-srcset` opener's attribute for add srcset to full image in opened lightbox. $( document ).on( 'lity:ready', function( event, instance ) { var $el = instance.element(), $opener = instance.opener(), srcset = typeof $opener !== 'undefined' ? $opener.data( 'lity-srcset' ) : ''; if ( typeof srcset !== 'undefined' && srcset !== '' ) { $el.find( '.lity-content img' ).attr( 'srcset', srcset ); } } ); }, //--------------------------------------------------------------------// // Helper functions. //--------------------------------------------------------------------// /** * Return if the target nodeName is a form element. * * @since 1.4.0 */ isFormTypeNode: function( name ) { name = name || false; if ( 'TEXTAREA' === name || 'INPUT' === name || 'SELECT' === name ) { return true; } return false; }, /** * Get query string in a URL. * * @since 1.3.9 */ getQueryString: function( name ) { var match = new RegExp( '[?&]' + name + '=([^&]*)' ).exec( window.location.search ); return match && decodeURIComponent( match[1].replace( /\+/g, ' ' ) ); }, /** * Debug output helper. * * @since 1.4.4 * @param msg */ debug: function( msg ) { if ( WPFormsAdmin.isDebug() ) { if ( typeof msg === 'object' || msg.constructor === Array ) { console.log( 'WPForms Debug:' ); console.log( msg ); } else { console.log( 'WPForms Debug: ' + msg ); } } }, /** * Is debug mode. * * @since 1.4.4 */ isDebug: function() { return ( window.location.hash && '#wpformsdebug' === window.location.hash ); }, /** * Get Delete / Trash all notice message. * * @since 1.8.5 * * @param {string} type Type of screen. * * @return {Object} Notice Data object. */ getDeleteAllNoticeData: ( type = '' ) => { // Define delete data for spam or trash. if ( [ 'spam', 'trash' ].includes( type ) ) { return { contentAll : wpforms_admin.entry_delete_all_confirm, content : wpforms_admin.entry_delete_n_confirm, action : 'delete', }; } // Otherwise define trash data. return { contentAll : wpforms_admin.entry_trash_all_confirm, content : wpforms_admin.entry_trash_n_confirm, action : 'trash', }; }, /** * Show/hide the right arrow for the scrollable menu on mobile devices. * * @since 1.8.8 */ initScrollableMenu() { $( document ).on( 'wpformsReady', function() { const $menu = $( '.wpforms-admin-tabs' ); if ( ! $menu.length ) { return; } const $lastMenuItem = $menu.find( 'li:last-child' ); // The last item of the menu is not visible - show the right arrow as an indicator of a scrollable menu. if ( ! wpf.isInViewport( $lastMenuItem ) ) { $menu.addClass( 'wpforms-admin-tabs--scrollable' ); } // Listen to `scroll` event in order to hide the right arrow when the last item is visible. $menu.on( 'scroll', function() { $menu.toggleClass( 'wpforms-admin-tabs--scrollable', ! wpf.isInViewport( $lastMenuItem ) ); } ); } ); }, }; WPFormsAdmin.init(); window.WPFormsAdmin = WPFormsAdmin; }( jQuery ) ); admin/admin.min.js000064400000114302151716470030010051 0ustar00!function(l){"use strict";var m,p={settings:{iconActivate:'',iconDeactivate:'',iconInstall:'',iconSpinner:'',mediaFrame:!1},init:function(){m=this.settings,l(p.ready),p.initEntriesSingle(),p.initEntriesList(),p.initWelcome(),l(document).on("wpformsReady",p.initAddons),p.initSettings(),p.initTools(),p.initUpgrades(),p.initScrollableMenu()},ready:function(){l.ajaxSetup({data:{_wp_http_referer:wpf.updateQueryString("_wp_http_referer",null)}}),p.scrollToIntegration(),l(".notice").show(),l("#screen-meta-links, #screen-meta").prependTo("#wpforms-header-temp").show(),p.initChoicesJS(),l(document).on("htmx:afterSwap",p.initChoicesJS),p.initCheckboxMultiselectColumns(),l(".wpforms-color-picker").each(function(){var e=l(this);e.minicolors({defaultValue:e.data("fallback-color")||""})}),l(".wpforms-file-upload").each(function(){var e=l(this).find("input[type=file]"),n=l(this).find("label"),o=n.html();e.on("change",function(e){var t="";this.files&&1'+t+"")):(e.parent().removeClass("checked"),n.find("#"+o).remove())}),l(document).on("click",".checkbox-multiselect-columns .all",function(e){e.preventDefault(),l(this).closest(".checkbox-multiselect-columns").find("input[type=checkbox]").prop("checked",!0).trigger("change"),l(this).remove()})},initFormOverview:function(){console.warn('WARNING! Function "WPFormsAdmin.initFormOverview()" has been deprecated, please use the new "WPFormsForms.Overview.init()" function instead!'),window.WPFormsForms.Overview.init()},initEntriesList(){l(document).on("click","#wpforms-entries-list .form-selector .toggle",function(e){e.preventDefault(),l(this).toggleClass("active").next(".form-list").toggle()}),l(document).on("click","#wpforms-entries-table #doaction",function(e){var t=l(this).closest("form"),n=t.find("table"),o=t.find("select[name=action]"),n=n.find("input[name^=entry_id]:checked");"delete"!==o.val()&&"trash"!==o.val()||!n.length||(o="delete"===o.val()?wpforms_admin.entry_delete_n_confirm:wpforms_admin.entry_trash_n_confirm,e.preventDefault(),l.confirm({title:wpforms_admin.heads_up,content:o.replace("{entry_count}",n.length),icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_admin.ok,btnClass:"btn-confirm",keys:["enter"],action:function(){t.trigger("submit")}},cancel:{text:wpforms_admin.cancel,keys:["esc"]}}}))}),l(document).on("click","#wpforms-entries-list .wp-list-table .delete",function(e){e.preventDefault();var t=l(this).attr("href");l.confirm({title:wpforms_admin.heads_up,content:wpforms_admin.entry_delete_confirm,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_admin.ok,btnClass:"btn-confirm",keys:["enter"],action:function(){window.location=t}},cancel:{text:wpforms_admin.cancel,keys:["esc"]}}})}),l(document).on("click","#wpforms-entries-list .wp-list-table .trash",function(e){e.preventDefault();const t=l(this).attr("href");l.confirm({title:wpforms_admin.heads_up,content:wpforms_admin.entry_trash_confirm,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_admin.ok,btnClass:"btn-confirm",keys:["enter"],action:()=>{window.location=t}},cancel:{text:wpforms_admin.cancel,keys:["esc"]}}})}),l(document).on("click","#wpforms-entries-list .wp-list-table .indicator-star",function(e){e.preventDefault();var e=l(this),t=l("#wpforms-entries-list .starred-num"),n=e.parents("table");let o="",i=Number(t.text());e.hasClass("star")?(o="star",i++,e.attr("title",wpforms_admin.entry_unstar)):(o="unstar",i--,e.attr("title",wpforms_admin.entry_star)),e.toggleClass("star unstar"),n.hasClass("wpforms-entries-table-spam")||n.hasClass("wpforms-entries-table-trash")||t.text(i);n={task:o,action:"wpforms_entry_list_star",nonce:wpforms_admin.nonce,entryId:e.data("id"),formId:e.data("form-id")};l.post(wpforms_admin.ajax_url,n)}),l(document).on("click","#wpforms-entries-list .wp-list-table .indicator-read",function(e){e.preventDefault();var e=l(this),t=l("#wpforms-entries-list .unread-num"),n=e.parents("table");let o="",i=Number(t.text());e.hasClass("read")?(o="read",i--,e.attr("title",wpforms_admin.entry_unread)):(o="unread",i++,e.attr("title",wpforms_admin.entry_read)),e.toggleClass("read unread"),n.hasClass("wpforms-entries-table-spam")||n.hasClass("wpforms-entries-table-trash")||t.text(i);n={task:o,action:"wpforms_entry_list_read",nonce:wpforms_admin.nonce,entryId:e.data("id"),formId:e.data("form-id")};l.post(wpforms_admin.ajax_url,n)}),l(document).on("click","#wpforms-entries-list .form-details-actions-removeall",function(e){e.preventDefault();const t=l(this).data("page"),n=p.getDeleteAllNoticeData(t),o=l(this).attr("href"),i=l("#wpforms-entries-table"),s=i.data("filtered-count-trash")&&"trash"===n.action?parseInt(i.data("filtered-count-trash"),10):0,a={action:"wpforms_entry_list_process_"+n.action+"_all",form_id:i.find('input[name="form_id"]').val(),date:i.find('input[name="date"]').val(),page:t,search:{field:i.find('select[name="search[field]"]').val(),comparison:i.find('select[name="search[comparison]"]').val(),term:i.find('input[name="search[term]"]').val()},nonce:wpforms_admin.nonce,url:o};l.confirm({title:wpforms_admin.heads_up,content:s&&l("#wpforms-reset-filter").length?n.content.replace("{entry_count}",s):n.contentAll,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_admin.ok,btnClass:"btn-confirm",keys:["enter"],action:()=>{l.get(wpforms_admin.ajax_url,a).done(function(e){e.success&&(window.location=_.isEmpty(e.data)?o:e.data)})}},cancel:{text:wpforms_admin.cancel,keys:["esc"]}}})}),l(document).on("heartbeat-send",function(e,t){var n,o=l("#wpforms-entries-list");o.length&&!o.find(".wpforms-dash-widget").length&&void 0!==(n=o.find("#wpforms-entries-table").data("last-entry-id"))&&(t.wpforms_new_entries_entry_id=n,t.wpforms_new_entries_form_id=o.find("input[name=form_id]").val())}),l(document).on("heartbeat-tick",function(e,t){var n,o,i=l("#wpforms-entries-list");i.length&&t.wpforms_new_entries_notification&&(n=i.find(".wp-list-table thead tr").first().children().length,i.find(".new-entries-notification").length||i.find(".wp-list-table thead").append(''),(o=i.find(".new-entries-notification a")).text(t.wpforms_new_entries_notification).slideDown({start:function(){o.css("display","block")},always:function(){o.css("display","block")}}))})},initEntriesSingle:function(){"wpforms-entries"===p.getQueryString("page")&&"details"===p.getQueryString("view")&&p.entryHotkeys(),l(document).on("click","#wpforms-entries-single .wpforms-entry-delete a",function(e){e.preventDefault();const t=l(this).attr("href");l.confirm({title:wpforms_admin.heads_up,content:wpforms_admin.entry_delete_confirm,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_admin.ok,btnClass:"btn-confirm",keys:["enter"],action:function(){window.location=t}},cancel:{text:wpforms_admin.cancel,keys:["esc"]}}})}),l(document).on("click","#wpforms-entries-single .trash",function(e){e.preventDefault();const t=l(this).attr("href");l.confirm({title:wpforms_admin.heads_up,content:wpforms_admin.entry_trash_confirm,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_admin.ok,btnClass:"btn-confirm",keys:["enter"],action:()=>{window.location=t}},cancel:{text:wpforms_admin.cancel,keys:["esc"]}}})}),l(document).on("click","#wpforms-entries-single .wpforms-entry-print a",function(e){e.preventDefault(),window.open(l(this).attr("href"))}),l(document).on("click","#wpforms-entries-single .wpforms-empty-field-toggle",function(e){e.preventDefault(),"true"===wpCookies.get("wpforms_entry_hide_empty")?(wpCookies.remove("wpforms_entry_hide_empty"),l(this).text(wpforms_admin.entry_empty_fields_hide)):(wpCookies.set("wpforms_entry_hide_empty","true",2592e3),l(this).text(wpforms_admin.entry_empty_fields_show)),l(".wpforms-entry-field.empty, .wpforms-edit-entry-field.empty").toggle()}),l(document).on("click","#wpforms-entries-single .wpforms-entry-notes-new .add",function(e){e.preventDefault(),l(this).hide().next("form").stop().slideToggle()}),l(document).on("click","#wpforms-entries-single .wpforms-entry-notes-new .cancel",function(e){e.preventDefault(),l(this).closest("form").stop().slideToggle(),l(".wpforms-entry-notes-new .add").show()}),l(document).on("click","#wpforms-entries-single .wpforms-entry-notes-byline .note-delete",function(e){e.preventDefault();var t=l(this).attr("href");l.confirm({title:wpforms_admin.heads_up,content:wpforms_admin.entry_note_delete_confirm,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_admin.ok,btnClass:"btn-confirm",keys:["enter"],action:function(){window.location=t}},cancel:{text:wpforms_admin.cancel,keys:["esc"]}}})})},entryHotkeys:function(){l(document).on("keydown",function(e){74!==e.keyCode||e.metaKey||p.isFormTypeNode(e.target.nodeName)?75!==e.keyCode||e.metaKey||p.isFormTypeNode(e.target.nodeName)||"#"!==(e=l("#wpforms-admin-single-navigation-next-link").attr("href"))&&(window.location.href=e):"#"!==(e=l("#wpforms-admin-single-navigation-prev-link").attr("href"))&&(window.location.href=e)})},initWelcome:function(){l(document).on("click","#wpforms-welcome .play-video",function(e){e.preventDefault();l.dialog({title:!1,content:'
    ',closeIcon:!0,boxWidth:"70%"})})},initAddons:function(){if(l("#wpforms-admin-addons").length){var n=l("#wpforms-addons-list-section-all"),o=l("#wpforms-addons-list-section-installed");if(n.length||o.length){let e,t;o.length&&(e=new List("wpforms-addons-list-section-installed",{valueNames:["addon-link"]})),n.length&&(t=new List("wpforms-addons-list-section-all",{valueNames:["addon-link"]})),l("#wpforms-addons-search").on("keyup search",function(){p.updateAddonSearchResult(this,t,e)})}l(document).on("change",".wpforms-addons-list-item .wpforms-toggle-control input",function(e){if(e.preventDefault(),l(this).hasClass("disabled"))return!1;p.addonToggleNew(l(this))}),l(document).on("click",".wpforms-addons-list-item button",function(e){if(e.preventDefault(),l(this).hasClass("disabled"))return!1;p.addonToggleNew(l(this))}),l(document).on("click","#wpforms-admin-addons .addon-item button",function(e){if(e.preventDefault(),l(this).hasClass("disabled"))return!1;p.addonToggle(l(this))})}},updateAddonSearchResult(e,t,n){let o=l(e).val();o=o.replace(/[.,]/g," ");var e=l("#wpforms-addons-no-results"),i=l("#wpforms-addons-list-section-all"),s=l("#wpforms-addons-list-section-installed"),t=t?t.search(o):[],n=n?n.search(o):[];e.toggle(0===t.length&&0===n.length),i.toggle(0

    ${"addon"===t?wpforms_admin.addon_error:wpforms_admin.plugin_error}

    `):s.append(``),"install"===d?(n=!1,p.removeSpinnerFromButton(i)):"deactivate"===d?n=!0:"activate"===d&&(n=!1)}p.setAddonState(e,d,t,function(e){var t;e.success?(t=e,"install"===d?(o=a.active,n=!0,s.attr("data-plugin",t.data.basename),t.data.is_activated||(o=a.installed,n=!1),i.hide(),i=i.closest(".wpforms-addons-list-item").find(".wpforms-toggle-control input")):"activate"===d?(s.find(".wpforms-addons-list-item-footer-settings-link").fadeIn(150),o=a.active,n=!0):"deactivate"===d&&(s.find(".wpforms-addons-list-item-footer-settings-link").fadeOut(150),o=a.installed,n=!1),s.removeClass(a.active+" "+a.incompatible+" "+a.installed+" "+a.missing).addClass(o)):r(e),p.updateAddonButtonPropertiesAndUI(i,c,s,a,n)},function(){r({data:wpforms_admin.server_error}),p.updateAddonButtonPropertiesAndUI(i,c,s,a,n)})}},addSpinnerToButton(e){var t=e.width();e.data("original-text",e.html()),e.width(t).html('')},removeSpinnerFromButton(e){e.html(e.data("original-text"))},getAddonState(e,t,n){return e.hasClass(t.active)||e.hasClass(t.incompatible)?"deactivate":e.hasClass(t.installed)?"activate":e.hasClass(t.missing)?(p.addSpinnerToButton(n),"install"):""},updateAddonButtonPropertiesAndUI(e,t,n,o,i){e.prop("checked",i),e.prop("disabled",!1),e.siblings(".wpforms-toggle-control-status").html(e.siblings(".wpforms-toggle-control-status").data(i?"on":"off")),0'+c+""),t.find("span.status-label").removeClass("status-active status-installed status-missing").addClass(i).removeClass("button button-primary button-secondary disabled").text(s),n.removeClass("status-active status-installed status-missing").removeClass("button button-primary button-secondary disabled").addClass(i).html(a)):("object"==typeof e.data?"addon"===d?t.find(".actions").append('

    '+wpforms_admin.addon_error+"

    "):t.find(".actions").append('

    '+wpforms_admin.plugin_error+"

    "):t.find(".actions").append('

    '+e.data+"

    "),"install"===o&&"plugin"===d&&n.addClass("status-go-to-url").removeClass("status-missing"),n.html(r)),n.prop("disabled",!1).removeClass("loading"),t.find(".actions").find(".msg.error").length||setTimeout(function(){l(".addon-item .msg").remove()},3e3)},function(e){console.log(e.responseText)})}},initSettings:function(){l(document).on("wpformsReady",function(){var e,t;l("#wpforms-settings").length&&(e=p.getQueryString("wpforms-integration"),t=p.getQueryString("jump"),e?l("body").animate({scrollTop:l("#wpforms-integration-"+e).offset().top},1e3):t&&l("body").animate({scrollTop:l("#"+t).offset().top},1e3),l(".wpforms-admin-settings-form").conditions([{conditions:{element:"#wpforms-setting-gdpr",type:"checked",operator:"is"},actions:{if:{element:"#wpforms-setting-row-gdpr-disable-uuid,#wpforms-setting-row-gdpr-disable-details",action:"show"},else:{element:"#wpforms-setting-row-gdpr-disable-uuid,#wpforms-setting-row-gdpr-disable-details",action:"hide"}},effect:"appear"},{conditions:{element:"input[name=captcha-provider]:checked",type:"value",operator:"=",condition:"hcaptcha"},actions:{if:[{element:".wpforms-setting-row",action:"show"},{element:".wpforms-setting-recaptcha, #wpforms-setting-row-recaptcha-site-key, #wpforms-setting-row-recaptcha-secret-key, #wpforms-setting-row-recaptcha-fail-msg, .wpforms-setting-turnstile, #wpforms-setting-row-turnstile-heading, #wpforms-setting-row-turnstile-site-key, #wpforms-setting-row-turnstile-secret-key, #wpforms-setting-row-turnstile-theme, #wpforms-setting-row-turnstile-fail-msg",action:"hide"}]},effect:"appear"},{conditions:{element:"input[name=captcha-provider]:checked",type:"value",operator:"=",condition:"recaptcha"},actions:{if:[{element:".wpforms-setting-row",action:"show"},{element:"#wpforms-setting-row-hcaptcha-heading, #wpforms-setting-row-hcaptcha-site-key, #wpforms-setting-row-hcaptcha-secret-key, #wpforms-setting-row-hcaptcha-fail-msg, #wpforms-setting-row-turnstile-heading, #wpforms-setting-row-turnstile-site-key, #wpforms-setting-row-turnstile-secret-key, #wpforms-setting-row-turnstile-theme, #wpforms-setting-row-turnstile-fail-msg",action:"hide"}]},effect:"appear"},{conditions:{element:"input[name=captcha-provider]:checked",type:"value",operator:"=",condition:"turnstile"},actions:{if:[{element:".wpforms-setting-row",action:"show"},{element:"#wpforms-setting-row-hcaptcha-heading, #wpforms-setting-row-hcaptcha-site-key, #wpforms-setting-row-hcaptcha-secret-key, #wpforms-setting-row-hcaptcha-fail-msg, .wpforms-setting-recaptcha, #wpforms-setting-row-recaptcha-site-key, #wpforms-setting-row-recaptcha-secret-key, #wpforms-setting-row-recaptcha-fail-msg",action:"hide"}]},effect:"appear"},{conditions:{element:"input[name=captcha-provider]:checked",type:"value",operator:"=",condition:"none"},actions:{if:[{element:".wpforms-setting-row",action:"hide"},{element:".wpforms-setting-captcha-heading, #wpforms-setting-row-captcha-provider",action:"show"}]},effect:"appear"}]))}),l(document).on("change","#wpforms-setting-row-render-engine input",p.settingsRenderEngineChange),l(document).on("change","#wpforms-setting-disable-css",function(){p.settingsFormStylesAlert(l(this).val())}),l(document).on("click",".wpforms-setting-row-image button",function(e){e.preventDefault(),l(this).hasClass("wpforms-setting-remove-image")?l(this).closest(".wpforms-setting-row-image").find("input").val("").attr("value","").trigger("change").end().find("img").remove():p.imageUploadModal(l(this))}),l(document).on("click","#wpforms-setting-license-key-verify",function(e){e.preventDefault(),p.licenseVerify(l(this))}),l(document).on("click",".wpforms-setting-license-wrapper",function(e){e.preventDefault();e=l("#wpforms-setting-license-key");e.length&&e.prop("disabled")&&p.licenseEditMessage()}),l(document).on("click","#wpforms-setting-license-key-deactivate",function(e){e.preventDefault(),p.licenseDeactivate(l(this))}),l(document).on("click","#wpforms-setting-license-key-refresh",function(e){e.preventDefault(),p.licenseRefresh(l(this))}),l(document).on("click",".wpforms-settings-provider-connect",function(e){e.preventDefault();e=l(this);p.integrationConnect(e)}),l(document).on("click",".wpforms-settings-provider-accounts-list .remove a",function(e){e.preventDefault(),p.integrationDisconnect(l(this))}),l(document).on("click",".wpforms-settings-provider:not(.focus-out) .wpforms-settings-provider-header",function(e){e.preventDefault();var t=l(this);t.parent().find(".wpforms-settings-provider-accounts").stop(!1,!0).slideToggle("",function(){t.parent().find(".wpforms-settings-provider-logo i").toggleClass("fa-chevron-right fa-chevron-down")})}),l(document).on("click",".wpforms-settings-provider-accounts-toggle a",function(e){e.preventDefault();e=l(this).parent().next(".wpforms-settings-provider-accounts-connect");e.find("input[type=text], input[type=password]").val(""),e.stop().slideToggle()}),l(document).on("change","#wpforms-setting-row-captcha-provider input",function(){var e=l("#wpforms-setting-row-captcha-preview");"hcaptcha"===this.value||"turnstile"===this.value?e.removeClass("wpforms-hidden"):"none"===this.value?e.addClass("wpforms-hidden"):l("#wpforms-setting-row-recaptcha-type input:checked").trigger("change"),e.find(".wpforms-captcha-preview").length&&(e.find(".wpforms-captcha-preview").empty(),e.find(".wpforms-captcha-placeholder").removeClass("wpforms-hidden"))}),l(document).on("change","#wpforms-setting-row-recaptcha-type input",function(){l("#wpforms-setting-row-captcha-preview").toggleClass("wpforms-hidden","v2"!==this.value),l("#wpforms-setting-row-recaptcha-v3-threshold").toggleClass("wpforms-hidden","v3"!==this.value)}),l(document).on("change",".wpforms-toggle-control input",function(){var e=l(this),t=e.is(":checked"),n=t?"on":"off",o=e.closest(".wpforms-setting-field"),e=e.closest(".wpforms-toggle-control").find(".wpforms-toggle-control-status"),i=o.find(".wpforms-toggle-desc.desc-on"),o=o.find(".wpforms-toggle-desc.desc-off"),s=0'),t.trigger("change")}).on("close",function(){m.mediaFrame.off("library:selection:add")}),m.mediaFrame.open()},licenseVerify:function(i){var s=i.closest(".wpforms-setting-row"),a=l("#wpforms-setting-license-key"),e=i.outerWidth(),r=i.text(),t={action:"wpforms_verify_license",nonce:wpforms_admin.nonce,license:a.val()};i.html(m.iconSpinner).css("width",e).prop("disabled",!0),l.post(wpforms_admin.ajax_url,t,function(e){var t,n="fa fa-check-circle",o="green";e.success?(t=e.data.msg,i.hide(),s.find("#wpforms-setting-license-key-info-message").empty().hide(),s.find(".type, .desc, #wpforms-setting-license-key-deactivate").show(),s.find(".type strong").text(e.data.type),l(".wpforms-license-notice").remove(),a.prop("disabled",!0).addClass("wpforms-setting-license-is-valid").attr("value",a.val())):(n="fa fa-exclamation-circle",o="orange",t=e.data,s.find(".type, .desc, #wpforms-setting-license-key-deactivate").hide(),a.prop("disabled",!1)),l.alert({title:t.header??!1,content:t.msg??t,icon:n,type:o,buttons:{confirm:{text:wpforms_admin.ok,btnClass:"btn-confirm",keys:["enter"]}}}),i.html(r).css("width","auto").prop("disabled",!1)}).fail(function(e){a.prop("disabled",!1),console.log(e.responseText)})},licenseEditMessage:function(){l.alert({title:wpforms_admin.heads_up,content:wpforms_admin.edit_license,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_admin.ok,btnClass:"btn-confirm",keys:["enter"]}}})},licenseDeactivate:function(e){const a=l(e),r=a.closest(".wpforms-setting-row");e=a.outerWidth();const c=a.text();var t={action:"wpforms_deactivate_license",nonce:wpforms_admin.nonce};a.html(m.iconSpinner).css("width",e).prop("disabled",!0),l.post(wpforms_admin.ajax_url,t,function(e){let t="fa fa-info-circle",n="blue",o=wpforms_admin.success;var i=e.data,s=i.msg&&"string"==typeof i.msg?i.msg:wpforms_admin.something_went_wrong;e.success?(r.find("#wpforms-setting-license-key").val("").attr("value","").prop({readonly:!1,disabled:!1}).removeClass(),r.find(".wpforms-license-key-deactivate-remove").remove(),r.find("#wpforms-setting-license-key-info-message").html(i.info).show(),r.find("#wpforms-setting-license-key-verify").prop("disabled",!1).show(),r.find(".type, .desc, #wpforms-setting-license-key-deactivate").hide()):(t="fa fa-exclamation-circle",n="orange",o=wpforms_admin.oops),l.alert({title:o,content:s,icon:t,type:n,buttons:{confirm:{text:wpforms_admin.ok,btnClass:"btn-confirm",keys:["enter"]}}}),a.html(c).css("width","auto").prop("disabled",!1)}).fail(function(e){console.log(e.responseText)})},licenseRefresh:function(e){var i=l(e).closest(".wpforms-setting-row"),s=l("#wpforms-setting-license-key"),e={action:"wpforms_refresh_license",nonce:wpforms_admin.nonce,license:s.val()};l.post(wpforms_admin.ajax_url,e,function(e){var t,n="fa fa-check-circle",o="green";e.success?(t=e.data.msg,i.find(".type strong").text(e.data.type)):(n="fa fa-exclamation-circle",o="orange",t=e.data,i.find(".type, .desc").hide(),s.removeClass("wpforms-setting-license-is-valid").addClass("wpforms-setting-license-is-invalid")),l.alert({title:t.header??!1,content:t.msg??t,icon:n,type:o,buttons:{confirm:{text:wpforms_admin.ok,btnClass:"btn-confirm",keys:["enter"]}}})}).fail(function(e){console.log(e.responseText)})},integrationConnect:function(t){var e=t.outerWidth(),n=t.text(),o=t.closest(".wpforms-settings-provider"),i={action:"wpforms_settings_provider_add_"+t.data("provider"),data:t.closest("form").serialize(),provider:t.data("provider"),nonce:wpforms_admin.nonce},s=wpforms_admin.provider_auth_error;t.html(wpforms_admin.connecting).css("width",e).prop("disabled",!0),l.post(wpforms_admin.ajax_url,i,function(e){e.success?(o.find(".wpforms-settings-provider-accounts-list ul").append(e.data.html),o.addClass("connected"),t.closest(".wpforms-settings-provider-accounts-connect").stop().slideToggle()):(Object.prototype.hasOwnProperty.call(e,"data")&&Object.prototype.hasOwnProperty.call(e.data,"error_msg")&&(s+="
    "+e.data.error_msg),p.integrationError(s))}).fail(function(){p.integrationError(s)}).always(function(){t.html(n).css("width","auto").prop("disabled",!1)})},integrationDisconnect:function(e){var n=l(e),o=n.parents(".wpforms-settings-provider"),t={action:"wpforms_settings_provider_disconnect_"+n.data("provider"),provider:n.data("provider"),key:n.data("key"),nonce:wpforms_admin.nonce},i=wpforms_admin.provider_delete_error;l.confirm({title:wpforms_admin.heads_up,content:wpforms_admin.provider_delete_confirm,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_admin.ok,btnClass:"btn-confirm",keys:["enter"],action:function(){l.post(wpforms_admin.ajax_url,t,function(e){var t;e.success?(n.parent().parent().remove(),void 0!==(t=o.find(".wpforms-settings-provider-accounts-list li").length)&&0!==t||o.removeClass("connected"),l(document).trigger("wpformsProviderRemoved",[o,e])):(Object.prototype.hasOwnProperty.call(e,"data")&&Object.prototype.hasOwnProperty.call(e.data,"error_msg")&&(i+="
    "+e.data.error_msg),p.integrationError(i))}).fail(function(){p.integrationError(i)})}},cancel:{text:wpforms_admin.cancel,keys:["esc"]}}})},integrationError:function(e){l.alert({title:wpforms_admin.something_went_wrong,content:e,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_admin.ok,btnClass:"btn-confirm",keys:["enter"]}}})},initTools(){l(document).on("change","#wpforms-tools-form-import, #wpforms-tools-form-other-import, #wpforms-tools-form-export, #wpforms-tools-form-template",function(){var e=l(this);e.parents("form").find("button").attr("aria-disabled",0===e.val().length)}),l(document).on("click","#wpforms-system-information-copy",function(e){e.preventDefault(),p.copySystemInformation()}),l(document).on("click","#wpforms-ssl-verify",function(e){e.preventDefault(),p.verifySSLConnection()}),l(document).on("click","#wpforms-recreate-tables",function(e){e.preventDefault(),p.recreateTables()}),l(document).on("click","#wpforms-importer-forms-submit",function(e){if(e.preventDefault(),l("#wpforms-importer-forms input:checked").length){const t=[];l("#wpforms-importer-forms input:checked").each(function(e){t[e]=l(this).val()}),wpforms_admin.isPro?p.importForms(t):p.analyzeForms(t)}else l.alert({title:wpforms_admin.heads_up,content:wpforms_admin.importer_forms_required,icon:"fa fa-info-circle",type:"blue",buttons:{confirm:{text:wpforms_admin.ok,btnClass:"btn-confirm",keys:["enter"]}}})}),l(document).on("click","#wpforms-importer-continue-submit",function(e){e.preventDefault(),p.importForms(m.formIDs)})},copySystemInformation(){l("#wpforms-system-information").select(),document.execCommand("copy")},verifySSLConnection(){const t=l("#wpforms-ssl-verify"),n=t.text(),o=t.outerWidth(),i=t.parent();t.css("width",o).prop("disabled",!0).text(wpforms_admin.testing);var e={action:"wpforms_verify_ssl",nonce:wpforms_admin.nonce};l.post(wpforms_admin.ajax_url,e,function(e){p.debug(e),i.find(".wpforms-notice").remove(),e.success&&t.before('
    '+e.data.msg+"
    "),!e.success&&e.data.msg&&t.before('
    '+e.data.msg+"
    "),!e.success&&e.data.debug&&t.before('
    '+e.data.debug+"
    "),t.css("width",o).prop("disabled",!1).text(n)})},recreateTables(){const t=l("#wpforms-recreate-tables"),e=t.text(),n=t.outerWidth(),o=t.parent();t.css("width",n).prop("disabled",!0).text(wpforms_admin.recreating);var i={action:"wpforms_recreate_tables",nonce:wpforms_admin.nonce};l.post(wpforms_admin.ajax_url,i,function(e){p.debug(e),o.find(".wpforms-notice").remove(),e.success&&(t.before('
    '+e.data.msg+"
    "),t.hide()),!e.success&&e.data.msg&&t.before('
    '+e.data.msg+"
    "),!e.success&&e.data.debug&&t.before('
    '+e.data.debug+"
    ")}).always(function(){t.css("width",n).prop("disabled",!1).text(e)})},analyzeForms:function(e){var t=l("#wpforms-importer-analyze");t.find(".form-total").text(e.length),t.find(".form-current").text("1"),l("#wpforms-importer-forms").hide(),t.show(),m.analyzeQueue=e,m.analyzed=0,m.analyzeUpgrade=[],m.formIDs=e,p.analyzeForm()},analyzeForm:function(){var t=l("#wpforms-importer-analyze"),n=_.first(m.analyzeQueue),e={action:"wpforms_import_form_"+p.getQueryString("provider"),analyze:1,form_id:n,nonce:wpforms_admin.nonce};l.post(wpforms_admin.ajax_url,e,function(e){e.success&&(_.isEmpty(e.data.upgrade_plain)&&_.isEmpty(e.data.upgrade_omit)||m.analyzeUpgrade.push({name:e.data.name,fields:_.union(e.data.upgrade_omit,e.data.upgrade_plain)}),m.analyzeQueue=_.without(m.analyzeQueue,n),m.analyzed++,_.isEmpty(m.analyzeQueue)?_.isEmpty(m.analyzeUpgrade)?p.importForms(m.formIDs):(e=wp.template("wpforms-importer-upgrade"),t.find(".upgrade").append(e(m.analyzeUpgrade)),t.find(".upgrade").show(),t.find(".process-analyze").hide()):(t.find(".form-current").text(m.analyzed+1),p.analyzeForm()))})},importForms:function(e){var t=l("#wpforms-importer-process");t.find(".form-total").text(e.length),t.find(".form-current").text("1"),l("#wpforms-importer-forms, #wpforms-importer-analyze").hide(),t.show(),m.importQueue=e,m.imported=0,p.importForm()},importForm:function(){var n=l("#wpforms-importer-process"),o=_.first(m.importQueue),e={action:"wpforms_import_form_"+p.getQueryString("provider"),form_id:o,nonce:wpforms_admin.nonce};l.post(wpforms_admin.ajax_url,e,function(e){var t;e.success&&(t=e.data.error?wp.template("wpforms-importer-status-error"):wp.template("wpforms-importer-status-update"),n.find(".status").prepend(t(e.data)),n.find(".status").show(),m.importQueue=_.without(m.importQueue,o),m.imported++,_.isEmpty(m.importQueue)?(n.find(".process-count").hide(),n.find(".forms-completed").text(m.imported),n.find(".process-completed").show()):(n.find(".form-current").text(m.imported+1),p.importForm()))})},initUpgrades:function(){l(document).on("click","#wpforms-upgrade-143 button",function(e){e.preventDefault();var t=l(this),e=t.outerWidth(),n=l("#wpforms-upgrade-143 .status"),o={action:"wpforms_upgrade_143",nonce:wpforms_admin.nonce,init:!0,incomplete:t.data("incomplete")};t.html(m.iconSpinner).css("width",e).prop("disabled",!0),l.post(wpforms_admin.ajax_url,o,function(e){e.success&&(m.upgraded=Number(e.data.upgraded),m.upgradeTotal=Number(e.data.total),e=Math.round(Number(m.upgraded)/Number(m.upgradeTotal)*100),t.remove(),n.find(".bar").css("width",e+"%"),n.show().find(".total").text(m.upgradeTotal),n.find(".current").text(m.upgraded),n.find(".percent").text(e+"%"),p.upgrade143())})})},upgrade143:function(){var n=l("#wpforms-upgrade-143 .status"),e={action:"wpforms_upgrade_143",nonce:wpforms_admin.nonce,upgraded:m.upgraded};l.post(wpforms_admin.ajax_url,e,function(e){var t;e.success&&(m.upgraded=Number(m.upgraded)+Number(e.data.count),t=Math.round(Number(m.upgraded)/Number(m.upgradeTotal)*100),n.find(".bar").css("width",t+"%"),Number(e.data.count)<10?(n.find(".progress-bar").addClass("complete"),n.find(".msg").text(wpforms_admin.upgrade_completed)):(n.find(".current").text(m.upgraded),n.find(".percent").text(t+"%"),p.upgrade143()))})},initFlyoutMenu:function(){var e,t,n,a,r,c=l("#wpforms-flyout");0!==c.length&&(e=c.find(".wpforms-flyout-head"),t=e.find("img"),n={state:"inactive",srcInactive:t.attr("src"),srcActive:t.data("active")},e.on("click",function(e){e.preventDefault(),"active"===n.state?(c.removeClass("opened"),t.attr("src",n.srcInactive),n.state="inactive"):(c.addClass("opened"),t.attr("src",n.srcActive),n.state="active")}),0!==(a=l("#wpfooter")).length)&&(r=l("#wpforms-overview, #wpforms-entries-list, #wpforms-tools.wpforms-tools-tab-action-scheduler, #wpforms-tools.wpforms-tools-tab-logs"),l(window).on("resize scroll",_.debounce(function(e){var t=a.offset().top,n=t+a.height(),o=0["spam","trash"].includes(e)?{contentAll:wpforms_admin.entry_delete_all_confirm,content:wpforms_admin.entry_delete_n_confirm,action:"delete"}:{contentAll:wpforms_admin.entry_trash_all_confirm,content:wpforms_admin.entry_trash_n_confirm,action:"trash"},initScrollableMenu(){l(document).on("wpformsReady",function(){const e=l(".wpforms-admin-tabs");if(e.length){const t=e.find("li:last-child");wpf.isInViewport(t)||e.addClass("wpforms-admin-tabs--scrollable"),e.on("scroll",function(){e.toggleClass("wpforms-admin-tabs--scrollable",!wpf.isInViewport(t))})}})}};p.init(),window.WPFormsAdmin=p}(jQuery);admin/builder/admin-builder-providers.js000064400000042016151716470030014356 0ustar00/* global wpforms_builder_providers, wpforms_builder, wpf, WPForms */ ( function( $ ) { var s; var WPFormsProviders = { settings: { spinner: '', spinnerWhite: '', }, /** * Start the engine. * * @since 1.0.0 */ init: function() { s = this.settings; // Document ready. $( WPFormsProviders.ready ); WPFormsProviders.bindUIActions(); }, /** * Document ready. * * @since 1.1.1 */ ready: function() { // Setup/cache some vars not available before. s.form = $( '#wpforms-builder-form' ); }, /** * Element bindings. * * @since 1.0.0 */ bindUIActions: function() { // Delete connection. $( document ).on( 'click', '.wpforms-provider-connection-delete', function( e ) { WPFormsProviders.connectionDelete( this, e ); } ); // Add new connection. $( document ).on( 'click', '.wpforms-provider-connections-add', function( e ) { WPFormsProviders.connectionAdd( this, e ); } ); // Add new provider account. $( document ).on( 'click', '.wpforms-provider-account-add button', function( e ) { WPFormsProviders.accountAdd( this, e ); } ); // Select provider account. $( document ).on( 'change', '.wpforms-provider-accounts select', function( e ) { WPFormsProviders.accountSelect( this, e ); } ); // Select account list. $( document ).on( 'change', '.wpforms-provider-lists select', function( e ) { WPFormsProviders.accountListSelect( this, e ); } ); $( document ).on( 'wpformsPanelSwitch', function( e, targetPanel ) { WPFormsProviders.providerPanelConfirm( targetPanel ); } ); // Alert users if they save a form and do not configure required // fields. $( document ).on( 'wpformsSaved', function() { var providerAlerts = []; var $connectionBlocks = $( '#wpforms-panel-providers' ).find( '.wpforms-connection-block' ); if ( ! $connectionBlocks.length ) { return; } $connectionBlocks.each( function() { var requiredEmpty = false, providerName; $( this ).find( 'table span.required' ).each( function() { var $element = $( this ).parent().parent().find( 'select' ); if ( $element.val() === '' ) { requiredEmpty = true; } } ); if ( requiredEmpty ) { var $titleArea = $( this ).closest( '.wpforms-panel-content-section' ).find( '.wpforms-panel-content-section-title' ).clone(); $titleArea.find( 'button' ).remove(); providerName = $titleArea.text().trim(); var msg = wpforms_builder.provider_required_flds; if ( -1 < providerAlerts.indexOf( providerName ) ) { return; } $.alert( { title: wpforms_builder.heads_up, content: msg.replace( '{provider}', providerName ), icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); providerAlerts.push( providerName ); } } ); } ); }, /** * Delete provider connection * * @since 1.0.0 */ connectionDelete: function( el, e ) { e.preventDefault(); var $this = $( el ); $.confirm( { title: false, content: wpforms_builder_providers.confirm_connection, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action: function() { const $section = $this.closest( '.wpforms-panel-content-section' ); $this.closest( '.wpforms-provider-connection' ).remove(); // Update sidebar icon near the provider. const provider = $this.closest( '.wpforms-provider-connection' ).data( 'provider' ), $sidebarItem = $( '.wpforms-panel-sidebar-section-' + provider ); $sidebarItem.find( '.fa-check-circle-o' ).toggleClass( 'wpforms-hidden', $( $section ).find( '.wpforms-provider-connection' ).length <= 0 ); if ( ! $section.find( '.wpforms-provider-connection' ).length ) { $section.find( '.wpforms-builder-provider-connections-default' ).removeClass( 'wpforms-hidden' ); } }, }, cancel: { text: wpforms_builder.cancel, }, }, } ); }, /** * Add new provider connection. * * @since 1.0.0 */ connectionAdd: function( el, e ) { e.preventDefault(); var $this = $( el ), $connections = $this.parent().parent(), $container = $this.parent(), provider = $this.data( 'provider' ), defaultValue = WPFormsProviders.getDefaultConnectionName( provider ).trim(), type = $this.data( 'type' ), namePrompt = wpforms_builder_providers.prompt_connection, nameField = '', nameError = '

    ' + wpforms_builder_providers.error_name + '

    ', modalContent = namePrompt + nameField + nameError; modalContent = modalContent.replace( /%type%/g, type ); $.confirm( { title: false, content: modalContent, icon: 'fa fa-info-circle', type: 'blue', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action: function() { var name = this.$content.find( 'input#provider-connection-name' ).val().trim(); var error = this.$content.find( '.error' ); if ( name === '' ) { error.show(); return false; } else { // Disable button. WPFormsProviders.inputToggle( $this, 'disable' ); // Fire AJAX. var data = { action : 'wpforms_provider_ajax_' + provider, provider: provider, task : 'new_connection', name : name, id : s.form.data( 'id' ), nonce : wpforms_builder.nonce, }; WPFormsProviders.fireAJAX( $this, data, function( res ) { if ( res.success ) { $connections.find( '.wpforms-builder-provider-connections-default' ).addClass( 'wpforms-hidden' ); $connections.find( '.wpforms-provider-connections' ).prepend( res.data.html ); // Process and load the accounts if they exist. var $connection = $connections.find( '.wpforms-provider-connection' ).first(); if ( $connection.find( '.wpforms-provider-accounts option:selected' ) ) { $connection.find( '.wpforms-provider-accounts option' ).first().prop( 'selected', true ); $connection.find( '.wpforms-provider-accounts select' ).trigger( 'change' ); } } else { WPFormsProviders.errorDisplay( res.data.error, $container ); } } ); } }, }, cancel: { text: wpforms_builder.cancel, }, }, } ); }, /** * Add and authorize provider account. * * @since 1.0.0 */ accountAdd: function( el, e ) { e.preventDefault(); var $this = $( el ), provider = $this.data( 'provider' ), $connection = $this.closest( '.wpforms-provider-connection' ), $container = $this.parent(), $fields = $container.find( ':input' ), errors = WPFormsProviders.requiredCheck( $fields, $container ); // Disable button. WPFormsProviders.inputToggle( $this, 'disable' ); // Bail if we have any errors. if ( errors ) { $this.prop( 'disabled', false ).find( 'i' ).remove(); return false; } // Fire AJAX. var data = { action : 'wpforms_provider_ajax_' + provider, provider : provider, connection_id: $connection.data( 'connection_id' ), task : 'new_account', data : WPFormsProviders.fakeSerialize( $fields ), }; WPFormsProviders.fireAJAX( $this, data, function( res ) { if ( res.success ) { $container.nextAll( '.wpforms-connection-block' ).remove(); $container.nextAll( '.wpforms-conditional-block' ).remove(); $container.after( res.data.html ); $container.slideUp(); $connection.find( '.wpforms-provider-accounts select' ).trigger( 'change' ); } else { WPFormsProviders.errorDisplay( res.data.error, $container ); } } ); }, /** * Selecting a provider account * * @since 1.0.0 */ accountSelect: function( el, e ) { e.preventDefault(); var $this = $( el ), $connection = $this.closest( '.wpforms-provider-connection' ), $container = $this.parent(), provider = $connection.data( 'provider' ); // Disable select, show loading. WPFormsProviders.inputToggle( $this, 'disable' ); // Remove any blocks that might exist as we prep for new account. $container.nextAll( '.wpforms-connection-block' ).remove(); $container.nextAll( '.wpforms-conditional-block' ).remove(); if ( ! $this.val() ) { // User selected to option to add new account. $connection.find( '.wpforms-provider-account-add input' ).val( '' ); $connection.find( '.wpforms-provider-account-add' ).slideDown(); WPFormsProviders.inputToggle( $this, 'enable' ); } else { $connection.find( '.wpforms-provider-account-add' ).slideUp(); // Fire AJAX. var data = { action : 'wpforms_provider_ajax_' + provider, provider : provider, connection_id: $connection.data( 'connection_id' ), task : 'select_account', account_id : $this.find( ':selected' ).val(), }; WPFormsProviders.fireAJAX( $this, data, function( res ) { if ( res.success ) { $container.after( res.data.html ); // Process first list found. $connection.find( '.wpforms-provider-lists option' ).first().prop( 'selected', true ); $connection.find( '.wpforms-provider-lists select' ).trigger( 'change' ); } else { WPFormsProviders.errorDisplay( res.data.error, $container ); } } ); } }, /** * Selecting a provider account list. * * @since 1.0.0 */ accountListSelect: function( el, e ) { e.preventDefault(); var $this = $( el ), $connection = $this.closest( '.wpforms-provider-connection' ), $container = $this.parent(), provider = $connection.data( 'provider' ); // Disable select, show loading. WPFormsProviders.inputToggle( $this, 'disable' ); // Remove any blocks that might exist as we prep for new account. $container.nextAll( '.wpforms-connection-block' ).remove(); $container.nextAll( '.wpforms-conditional-block' ).remove(); var data = { action : 'wpforms_provider_ajax_' + provider, provider : provider, connection_id: $connection.data( 'connection_id' ), task : 'select_list', account_id : $connection.find( '.wpforms-provider-accounts option:selected' ).val(), list_id : $this.find( ':selected' ).val(), form_id : s.form.data( 'id' ), }; WPFormsProviders.fireAJAX( $this, data, function( res ) { if ( res.success ) { $container.after( res.data.html ); // Re-init tooltips for new fields. wpf.initTooltips(); } else { WPFormsProviders.errorDisplay( res.data.error, $container ); } } ); }, /** * Confirm form save before loading Provider panel. * If confirmed, save and reload panel. * * @since 1.0.0 */ providerPanelConfirm: function( targetPanel ) { wpforms_panel_switch = true; if ( targetPanel === 'providers' && ! s.form.data( 'revision' ) ) { if ( wpf.savedState != wpf.getFormState( '#wpforms-builder-form' ) ) { wpforms_panel_switch = false; $.confirm( { title: false, content: wpforms_builder_providers.confirm_save, icon: 'fa fa-info-circle', type: 'blue', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action: function() { $( '#wpforms-save' ).trigger( 'click' ); $( document ).on( 'wpformsSaved', function() { let wpforms_builder_provider_url = wpforms_builder_providers.url; const $section = $( `#wpforms-panel-${ targetPanel } .wpforms-panel-sidebar-section.active` ); const section = $section.length && $section.data( 'section' ) !== 'default' ? $section.data( 'section' ) : null; // Adding an active section parameter. if ( section ) { wpforms_builder_provider_url += `§ion=${ section }`; } window.location.href = wpforms_builder_provider_url; } ); }, }, cancel: { text: wpforms_builder.cancel, }, }, } ); } } }, //--------------------------------------------------------------------// // Helper functions. //--------------------------------------------------------------------// /** * Fire AJAX call. * * @since 1.0.0 */ fireAJAX: function( el, d, success ) { var $this = $( el ); var data = { id : $( '#wpforms-builder-form' ).data( 'id' ), nonce : wpforms_builder.nonce, }; $.extend( data, d ); $.post( wpforms_builder.ajax_url, data, function( res ) { success( res ); WPFormsProviders.inputToggle( $this, 'enable' ); } ).fail( function( xhr, textStatus, e ) { console.log( xhr.responseText ); } ); }, /** * Toggle input with loading indicator. * * @since 1.0.0 */ inputToggle: function( el, status ) { var $this = $( el ); if ( status === 'enable' ) { if ( $this.is( 'select' ) ) { $this.prop( 'disabled', false ).next( 'i' ).remove(); } else { $this.prop( 'disabled', false ).find( 'i' ).remove(); } } else if ( status === 'disable' ) { if ( $this.is( 'select' ) ) { $this.prop( 'disabled', true ).after( s.spinner ); } else { $this.prop( 'disabled', true ).prepend( s.spinnerWhite ); } } }, /** * Display error. * * @since 1.0.0 */ errorDisplay: function( msg, location ) { location.find( '.wpforms-error-msg' ).remove(); location.prepend( '

    ' + msg + '

    ' ); }, /** * Check for required fields. * * @since 1.0.0 */ requiredCheck: function( fields, location ) { var error = false; // Remove any previous errors. location.find( '.wpforms-alert-required' ).remove(); // Loop through input fields and check for values. fields.each( function( index, el ) { if ( $( el ).hasClass( 'wpforms-required' ) && $( el ).val().length === 0 ) { $( el ).addClass( 'wpforms-error' ); error = true; } else { $( el ).removeClass( 'wpforms-error' ); } } ); if ( error ) { location.prepend( '

    ' + wpforms_builder_providers.required_field + '

    ' ); } return error; }, /** * Pseudo serializing. Fake it until you make it. * * @since 1.0.0 */ fakeSerialize: function( els ) { var fields = els.clone(); fields.each( function( index, el ) { if ( $( el ).data( 'name' ) ) { $( el ).attr( 'name', $( el ).data( 'name' ) ); } } ); return fields.serialize(); }, /** * Get the default name for a new connection. * * @since 1.9.3 * * @param {string} provider Current provider slug. * * @return {string} Returns the default name for a new connection. */ getDefaultConnectionName( provider ) { const providerClass = WPFormsProviders.getProviderClass( provider ); // Check if the provider has a method to set the custom connection name. if ( typeof providerClass?.setDefaultModalValue === 'function' ) { return providerClass.setDefaultModalValue(); } const providerName = $( `#${ provider }-provider` ).data( 'provider-name' ); const numberOfConnections = WPFormsProviders.getCountConnectionsOf( provider ); const defaultName = `${ providerName } ${ wpforms_builder.connection_label }`; if ( numberOfConnections === 0 ) { return defaultName; } return `${ defaultName } #${ numberOfConnections + 1 }`; }, /** * Get the number of connections for the provider. * * @since 1.9.3 * * @param {string} provider Current provider slug. * * @return {number} Returns the number of connections for the provider. */ getCountConnectionsOf( provider ) { return $( `#${ provider }-provider .wpforms-provider-connection` ).length; }, /** * Get a provider JS object. * * @since 1.9.3 * * @param {string} provider Provider name. * * @return {Object|null} Return provider object or null. */ getProviderClass( provider ) { const upperProviderPart = ( providerPart ) => ( providerPart.charAt( 0 ).toUpperCase() + providerPart.slice( 1 ) ); const getClassName = provider.split( '-' ).map( upperProviderPart ).join( '' ); if ( typeof WPForms?.Admin?.Builder?.Providers?.[ getClassName ] === 'undefined' ) { return null; } return WPForms.Admin.Builder.Providers[ getClassName ]; }, }; WPFormsProviders.init(); } )( jQuery ); admin/builder/admin-builder-providers.min.js000064400000020251151716470030015135 0ustar00!function(c){var s,a={settings:{spinner:'',spinnerWhite:''},init:function(){s=this.settings,c(a.ready),a.bindUIActions()},ready:function(){s.form=c("#wpforms-builder-form")},bindUIActions:function(){c(document).on("click",".wpforms-provider-connection-delete",function(e){a.connectionDelete(this,e)}),c(document).on("click",".wpforms-provider-connections-add",function(e){a.connectionAdd(this,e)}),c(document).on("click",".wpforms-provider-account-add button",function(e){a.accountAdd(this,e)}),c(document).on("change",".wpforms-provider-accounts select",function(e){a.accountSelect(this,e)}),c(document).on("change",".wpforms-provider-lists select",function(e){a.accountListSelect(this,e)}),c(document).on("wpformsPanelSwitch",function(e,o){a.providerPanelConfirm(o)}),c(document).on("wpformsSaved",function(){var r=[],e=c("#wpforms-panel-providers").find(".wpforms-connection-block");e.length&&e.each(function(){var e,o,n=!1;c(this).find("table span.required").each(function(){""===c(this).parent().parent().find("select").val()&&(n=!0)}),n&&((e=c(this).closest(".wpforms-panel-content-section").find(".wpforms-panel-content-section-title").clone()).find("button").remove(),e=e.text().trim(),o=wpforms_builder.provider_required_flds,-1')+('

    '+wpforms_builder_providers.error_name+"

    ")).replace(/%type%/g,e);c.confirm({title:!1,content:o,icon:"fa fa-info-circle",type:"blue",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"],action:function(){var e=this.$content.find("input#provider-connection-name").val().trim(),o=this.$content.find(".error");if(""===e)return o.show(),!1;a.inputToggle(n,"disable");o={action:"wpforms_provider_ajax_"+t,provider:t,task:"new_connection",name:e,id:s.form.data("id"),nonce:wpforms_builder.nonce};a.fireAJAX(n,o,function(e){var o;e.success?(r.find(".wpforms-builder-provider-connections-default").addClass("wpforms-hidden"),r.find(".wpforms-provider-connections").prepend(e.data.html),(o=r.find(".wpforms-provider-connection").first()).find(".wpforms-provider-accounts option:selected")&&(o.find(".wpforms-provider-accounts option").first().prop("selected",!0),o.find(".wpforms-provider-accounts select").trigger("change"))):a.errorDisplay(e.data.error,i)})}},cancel:{text:wpforms_builder.cancel}}})},accountAdd:function(e,o){o.preventDefault();var o=c(e),e=o.data("provider"),n=o.closest(".wpforms-provider-connection"),r=o.parent(),i=r.find(":input"),t=a.requiredCheck(i,r);if(a.inputToggle(o,"disable"),t)return o.prop("disabled",!1).find("i").remove(),!1;t={action:"wpforms_provider_ajax_"+e,provider:e,connection_id:n.data("connection_id"),task:"new_account",data:a.fakeSerialize(i)};a.fireAJAX(o,t,function(e){e.success?(r.nextAll(".wpforms-connection-block").remove(),r.nextAll(".wpforms-conditional-block").remove(),r.after(e.data.html),r.slideUp(),n.find(".wpforms-provider-accounts select").trigger("change")):a.errorDisplay(e.data.error,r)})},accountSelect:function(e,o){o.preventDefault();var o=c(e),n=o.closest(".wpforms-provider-connection"),r=o.parent(),e=n.data("provider");a.inputToggle(o,"disable"),r.nextAll(".wpforms-connection-block").remove(),r.nextAll(".wpforms-conditional-block").remove(),o.val()?(n.find(".wpforms-provider-account-add").slideUp(),e={action:"wpforms_provider_ajax_"+e,provider:e,connection_id:n.data("connection_id"),task:"select_account",account_id:o.find(":selected").val()},a.fireAJAX(o,e,function(e){e.success?(r.after(e.data.html),n.find(".wpforms-provider-lists option").first().prop("selected",!0),n.find(".wpforms-provider-lists select").trigger("change")):a.errorDisplay(e.data.error,r)})):(n.find(".wpforms-provider-account-add input").val(""),n.find(".wpforms-provider-account-add").slideDown(),a.inputToggle(o,"enable"))},accountListSelect:function(e,o){o.preventDefault();var o=c(e),e=o.closest(".wpforms-provider-connection"),n=o.parent(),r=e.data("provider"),r=(a.inputToggle(o,"disable"),n.nextAll(".wpforms-connection-block").remove(),n.nextAll(".wpforms-conditional-block").remove(),{action:"wpforms_provider_ajax_"+r,provider:r,connection_id:e.data("connection_id"),task:"select_list",account_id:e.find(".wpforms-provider-accounts option:selected").val(),list_id:o.find(":selected").val(),form_id:s.form.data("id")});a.fireAJAX(o,r,function(e){e.success?(n.after(e.data.html),wpf.initTooltips()):a.errorDisplay(e.data.error,n)})},providerPanelConfirm:function(n){wpforms_panel_switch=!0,"providers"!==n||s.form.data("revision")||wpf.savedState!=wpf.getFormState("#wpforms-builder-form")&&(wpforms_panel_switch=!1,c.confirm({title:!1,content:wpforms_builder_providers.confirm_save,icon:"fa fa-info-circle",type:"blue",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"],action:function(){c("#wpforms-save").trigger("click"),c(document).on("wpformsSaved",function(){let e=wpforms_builder_providers.url;var o=c(`#wpforms-panel-${n} .wpforms-panel-sidebar-section.active`),o=o.length&&"default"!==o.data("section")?o.data("section"):null;o&&(e+="§ion="+o),window.location.href=e})}},cancel:{text:wpforms_builder.cancel}}}))},fireAJAX:function(e,o,n){var r=c(e),e={id:c("#wpforms-builder-form").data("id"),nonce:wpforms_builder.nonce};c.extend(e,o),c.post(wpforms_builder.ajax_url,e,function(e){n(e),a.inputToggle(r,"enable")}).fail(function(e,o,n){console.log(e.responseText)})},inputToggle:function(e,o){e=c(e);"enable"===o?(e.is("select")?e.prop("disabled",!1).next("i"):e.prop("disabled",!1).find("i")).remove():"disable"===o&&(e.is("select")?e.prop("disabled",!0).after(s.spinner):e.prop("disabled",!0).prepend(s.spinnerWhite))},errorDisplay:function(e,o){o.find(".wpforms-error-msg").remove(),o.prepend('

    '+e+"

    ")},requiredCheck:function(e,o){var n=!1;return o.find(".wpforms-alert-required").remove(),e.each(function(e,o){c(o).hasClass("wpforms-required")&&0===c(o).val().length?(c(o).addClass("wpforms-error"),n=!0):c(o).removeClass("wpforms-error")}),n&&o.prepend('

    '+wpforms_builder_providers.required_field+"

    "),n},fakeSerialize:function(e){e=e.clone();return e.each(function(e,o){c(o).data("name")&&c(o).attr("name",c(o).data("name"))}),e.serialize()},getDefaultConnectionName(e){var o=a.getProviderClass(e);return"function"==typeof o?.setDefaultModalValue?o.setDefaultModalValue():(o=c(`#${e}-provider`).data("provider-name"),e=a.getCountConnectionsOf(e),o=o+" "+wpforms_builder.connection_label,0===e?o:o+" #"+(e+1))},getCountConnectionsOf(e){return c(`#${e}-provider .wpforms-provider-connection`).length},getProviderClass(e){e=e.split("-").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join("");return void 0===WPForms?.Admin?.Builder?.Providers?.[e]?null:WPForms.Admin.Builder.Providers[e]}};a.init()}(jQuery);admin/builder/admin-builder.js000064400001067461151716470030012357 0ustar00/* global wpforms_builder, wpf, jconfirm, wpforms_panel_switch, Choices, WPForms, WPFormsFormEmbedWizard, wpCookies, tinyMCE, WPFormsUtils, List, wpforms_preset_choices */ /** * @param wpforms_builder.smart_tags_disabled_for_confirmations */ /* noinspection JSUnusedLocalSymbols */ /* eslint-disable no-unused-expressions, no-shadow */ // noinspection ES6ConvertVarToLetConst var WPFormsBuilder = window.WPFormsBuilder || ( function( document, window, $ ) { // eslint-disable-line no-var let s, $builder; const elements = {}, browser = {}; /** * Whether to show the close confirmation dialog or not. * * @since 1.6.0 * * @type {boolean} */ let closeConfirmation = true; /** * A field is adding. * * @since 1.7.1 * * @type {boolean} */ let adding = false; /** * Preview tab. * * @since 1.9.4 * * @type {object|null} */ let previewTab = null; // noinspection JSUnusedGlobalSymbols const app = { /* eslint-disable camelcase */ settings: { spinner: '', spinnerInline: '', tinymceDefaults: { tinymce: { toolbar1: 'bold,italic,underline,blockquote,strikethrough,bullist,numlist,alignleft,aligncenter,alignright,undo,redo,link' }, quicktags: true, }, pagebreakTop: false, pagebreakBottom: false, upload_img_modal: false, choicesLimit: 20, // Choices limit for fields different from Dropdown. choicesLimitLong: 250, // Choices limit for Dropdown field. }, /** * Start the engine. * * @since 1.0.0 */ init() { const that = this; wpforms_panel_switch = true; s = this.settings; // Document ready. $( app.ready ); // Page load. $( window ).on( 'load', function() { // In the case of jQuery 3.+, we need to wait for a ready event first. if ( typeof $.ready.then === 'function' ) { $.ready.then( app.load ); } else { app.load(); } } ); $( window ).on( 'beforeunload', function() { if ( ! that.formIsSaved() && closeConfirmation ) { return wpforms_builder.are_you_sure_to_close; } } ); }, /** * Page load. * * @since 1.0.0 * @since 1.7.9 Added `wpformsBuilderReady` hook. * * @return {false|void} False if default event is prevented. */ load() { // Trigger initial save for new forms. if ( wpf.getQueryString( 'newform' ) ) { app.formSave( false ); } const panel = $( '#wpforms-panels-toggle .active' ).data( 'panel' ); // Render form preview on the Revisions panel if the panel is active. if ( panel === 'revisions' ) { app.updateRevisionPreview(); } // Allow callbacks to prevent making Form Builder ready... const event = WPFormsUtils.triggerEvent( $builder, 'wpformsBuilderReady' ); // ...by triggering `event.preventDefault()`. if ( event.isDefaultPrevented() ) { return false; } // Hide loading overlay and make the Form Builder ready to use. app.hideLoadingOverlay(); // Maybe display informational modal. // noinspection JSUnresolvedReference, EqualityComparisonWithCoercionJS if ( wpforms_builder.template_modal_display == '1' && 'fields' === wpf.getQueryString( 'view' ) ) { // eslint-disable-line $.alert( { title: wpforms_builder.template_modal_title, content: wpforms_builder.template_modal_msg, icon: 'fa fa-info-circle', type: 'blue', buttons: { confirm: { text: wpforms_builder.close, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); } }, /** * Init elements cache. * * @since 1.9.2 */ initElementsCache() { // Cache builder element. $builder = $( '#wpforms-builder' ); browser.isWindows = /Win/.test( navigator.userAgent ); browser.isLinux = /Linux/.test( navigator.userAgent ); browser.isMac = /Mac/.test( navigator.userAgent ); // Action buttons. elements.$helpButton = $( '#wpforms-help' ); elements.$previewButton = $( '#wpforms-preview-btn' ); elements.$embedButton = $( '#wpforms-embed' ); elements.$saveButton = $( '#wpforms-save' ); elements.$exitButton = $( '#wpforms-exit' ); // Cache other elements. elements.$noFieldsOptions = $( '#wpforms-panel-fields .wpforms-no-fields-holder .no-fields' ); elements.$noFieldsPreview = $( '#wpforms-panel-fields .wpforms-no-fields-holder .no-fields-preview' ); elements.$formPreview = $( '#wpforms-panel-fields .wpforms-preview-wrap' ); elements.$revisionPreview = $( '#wpforms-panel-revisions .wpforms-panel-content' ); elements.defaultEmailSelector = '.wpforms-field-option-email .wpforms-field-option-row-default_value input'; elements.$defaultEmail = $( elements.defaultEmailSelector ); elements.$focusOutTarget = null; elements.$nextFieldId = $( '#wpforms-field-id' ); elements.$addFieldsTab = $( '#add-fields a' ); elements.$fieldOptions = $( '#wpforms-field-options' ); elements.$fieldsPreviewWrap = $( '#wpforms-panel-fields .wpforms-panel-content-wrap' ); elements.$sortableFieldsWrap = $( '#wpforms-panel-fields .wpforms-field-wrap' ); elements.$addFieldsButtons = $( '.wpforms-add-fields-button' ).not( '.not-draggable' ).not( '.warning-modal' ).not( '.education-modal' ); elements.$fieldsSidebar = $( '#wpforms-panel-fields .wpforms-add-fields' ); elements.$searchInput = $( '#wpforms-search-fields-input' ); elements.$sidebarToggle = $( '.wpforms-panels .wpforms-panel-sidebar-content .wpforms-panel-sidebar-toggle' ); }, /** * Document ready. * * @since 1.0.0 */ ready() { // eslint-disable-line max-lines-per-function if ( app.isVisitedViaBackButton() ) { location.reload(); return; } app.initElementsCache(); // Add `_wp_http_referer` to the data of every AJAX request. $.ajaxSetup( { data: { // eslint-disable-next-line camelcase _wp_http_referer: wpf.updateQueryString( '_wp_http_referer', null ), }, } ); // Remove Embed button if builder opened in the popup. if ( app.isBuilderInPopup() ) { elements.$embedButton.remove(); elements.$previewButton.addClass( 'wpforms-alone' ); } app.loadMsWinCSS(); // Bind all actions. app.bindUIActions(); // Setup/cache some vars not available before s.formID = $( '#wpforms-builder-form' ).data( 'id' ); s.pagebreakTop = $( '.wpforms-pagebreak-top' ).length; s.pagebreakBottom = $( '.wpforms-pagebreak-bottom' ).length; // Disable implicit submission for every form inside the builder. // All form values are managed by JS and should not be submitted by pressing Enter. $builder.on( 'keypress', '#wpforms-builder-form :input:not(textarea)', function( e ) { if ( e.keyCode === 13 ) { e.preventDefault(); } } ); app.determineActiveSections(); app.loadEntryPreviewFields(); // Drag and drop sortable elements. app.fieldChoiceSortable( 'select' ); app.fieldChoiceSortable( 'radio' ); app.fieldChoiceSortable( 'checkbox' ); app.fieldChoiceSortable( 'payment-multiple' ); app.fieldChoiceSortable( 'payment-checkbox' ); app.fieldChoiceSortable( 'payment-select' ); // Set field group visibility. $( '.wpforms-add-fields-group' ).each( function( index, el ) { // eslint-disable-line no-unused-vars app.fieldGroupToggle( $( this ), 'load' ); } ); app.registerTemplates(); // Trim long form titles. app.trimFormTitle(); // Load Tooltips. wpf.initTooltips(); // Load Color Pickers. app.loadColorPickers(); // Hide/Show CAPTCHA in form. app.captchaToggle(); // Confirmations' initial setup. app.confirmationsSetup(); // Notification settings. app.notificationToggle(); app.notificationsByStatusAlerts(); app.notificationsUpdateElementsVisibility(); // Secret builder hotkeys. app.builderHotkeys(); // jquery-confirm defaults. jconfirm.defaults = { closeIcon: false, backgroundDismiss: false, escapeKey: true, animationBounce: 1, useBootstrap: false, theme: 'modern', boxWidth: '400px', animateFromElement: false, content: wpforms_builder.something_went_wrong, }; app.dropdownField.init(); app.iconChoices.init(); app.disabledFields.init(); app.checkEmptyDynamicChoices(); app.initSomeFieldOptions(); app.dismissNotice(); wpf.initializeChoicesEventHandlers(); }, checkEmptyDynamicChoices() { const choices = wpf.orders.choices || {}; if ( ! Object.keys( choices ).length ) { return; } wpf.orders.fields.forEach( function( fieldId ) { // eslint-disable-line complexity const isDynamic = app.dropdownField.helpers.isDynamicChoices( fieldId ); if ( ! isDynamic ) { return; } const $fieldPreview = $( '#wpforms-field-' + fieldId ); const type = app.dropdownField.helpers.getDynamicChoicesOptionType( fieldId ); const source = app.dropdownField.helpers.getDynamicChoicesOptionSource( fieldId ); const isModern = app.dropdownField.helpers.isDynamicChoicesOptionModern( fieldId ); let isEmpty = isModern ? $fieldPreview.find( '.has-no-choices' ).length : $fieldPreview.find( '.primary-input option:not(.placeholder), .primary-input li' ).length === 0; if ( isModern && ! isEmpty ) { const placeholder = $( '#wpforms-field-option-' + fieldId + '-placeholder' ).val(); const choices = app.dropdownField.helpers.getInitialChoices( fieldId ); isEmpty = choices.length === 1 && choices[ 0 ].label === placeholder && choices[ 0 ].placeholder === true; } if ( isEmpty ) { app.emptyChoicesNotice( fieldId, source, type ); } } ); }, /** * Load Microsoft Windows specific stylesheet. * * @since 1.6.8 */ loadMsWinCSS() { // Detect OS & browsers. if ( browser.isMac ) { return; } $( '' ) .appendTo( 'head' ) .attr( { type: 'text/css', rel: 'stylesheet', href: wpforms_builder.scrollbars_css_url, } ); }, /** * Builder was visited via back button in the browser. * * @since 1.6.5 * * @return {boolean} True if the builder was visited via back button in browser. */ isVisitedViaBackButton() { if ( ! performance ) { return false; } let isVisitedViaBackButton = false; performance.getEntriesByType( 'navigation' ).forEach( function( nav ) { if ( nav.type === 'back_forward' ) { isVisitedViaBackButton = true; } } ); return isVisitedViaBackButton; }, /** * Remove loading overlay. * * @since 1.6.8 */ hideLoadingOverlay() { const $overlay = $( '#wpforms-builder-overlay' ); $overlay.addClass( 'fade-out' ); setTimeout( function() { $overlay.hide(); }, 250 ); }, /** * Show loading overlay. * * @since 1.6.8 */ showLoadingOverlay() { const $overlay = $( '#wpforms-builder-overlay' ); $overlay.removeClass( 'fade-out' ); $overlay.show(); }, /** * Initialize some fields options controls. * * @since 1.6.3 */ initSomeFieldOptions() { // Show a toggled options groups. app.toggleAllOptionGroups( $builder ); // Date/Time field Date type option. $builder.find( '.wpforms-field-option-row-date .type select' ).trigger( 'change' ); }, /** * Dropdown field component. * * @since 1.6.1 */ dropdownField: { /** * Field configuration. * * @since 1.6.1 */ config: { modernClass: 'choicesjs-select', args: { searchEnabled: false, searchChoices: false, renderChoiceLimit: 1, shouldSort: false, callbackOnInit() { const $element = $( this.containerOuter.element ), $previewSelect = $element.closest( '.wpforms-field' ).find( 'select' ); // Turn off disabled styles. if ( $element.hasClass( 'is-disabled' ) ) { $element.removeClass( 'is-disabled' ); } // Disable instances on the preview panel. if ( $previewSelect.is( '[readonly]' ) ) { this.disable(); $previewSelect.prop( 'disabled', false ); } if ( this.passedElement.element.multiple ) { // Hide a placeholder if field has selected choices. if ( this.getValue( true ).length ) { $( this.input.element ).addClass( 'choices__input--hidden' ); } } // Decode allowed HTML entities for choices. $element.find( '.choices__item--selectable' ).each( function() { const $choice = $( this ); const text = wpf.decodeAllowedHTMLEntities( $choice.text() ); $choice.text( text ); } ); }, }, }, /** * Initialization for field component. * * @since 1.6.1 */ init() { // Choices.js init. $builder.find( '.' + app.dropdownField.config.modernClass ).each( function() { app.dropdownField.events.choicesInit( $( this ) ); } ); // Multiple option. $builder.on( 'change', '.wpforms-field-option-select .wpforms-field-option-row-multiple input', app.dropdownField.events.multiple ); // Style option. $builder.on( 'change', '.wpforms-field-option-select .wpforms-field-option-row-style select, .wpforms-field-option-payment-select .wpforms-field-option-row-style select', app.dropdownField.events.applyStyle ); // Add the ability to close the drop-down menu. $builder.on( 'click', '.choices', function( e ) { const $choices = $( this ), choicesObj = $choices.find( 'select' ).data( 'choicesjs' ); if ( choicesObj && $choices.hasClass( 'is-open' ) && e.target.classList.contains( 'choices__inner' ) ) { choicesObj.hideDropdown(); } } ); }, /** * Field events. * * @since 1.6.1 */ events: { /** * Load Choices.js library. * * @since 1.6.1 * * @param {Object} $element jQuery element selector. */ choicesInit( $element ) { const useAjax = $element.data( 'choicesjs-use-ajax' ) === 1; let instance; if ( $element.data( 'choicesjs-callback-fn' ) === 'select_pages' ) { instance = WPForms.Admin.Builder.WPFormsChoicesJS.setup( $element[ 0 ], app.dropdownField.config.args, { action: 'wpforms_ajax_search_pages_for_dropdown', nonce: useAjax ? wpforms_builder.nonce : null, } ); } else { instance = new Choices( $element[ 0 ], app.dropdownField.config.args ); } app.dropdownField.helpers.setInstance( $element, instance ); app.dropdownField.helpers.addPlaceholderChoice( $element, instance ); $element.closest( '.choices' ).toggleClass( 'wpforms-hidden', ! instance.config.choices.length ); }, /** * Multiple option callback. * * @since 1.6.1 * * @param {Object} event Event object. */ multiple( event ) { const fieldId = $( this ).closest( '.wpforms-field-option-row-multiple' ).data().fieldId, $primary = app.dropdownField.helpers.getPrimarySelector( fieldId ), $optionChoicesItems = $( '#wpforms-field-option-row-' + fieldId + '-choices input.default' ), $placeholder = $primary.find( '.placeholder' ), isDynamicChoices = app.dropdownField.helpers.isDynamicChoices( fieldId ), isMultiple = event.target.checked, choicesType = isMultiple ? 'checkbox' : 'radio'; // Add/remove a `multiple` attribute. $primary.prop( 'multiple', isMultiple ); // Change a `Choices` fields type: // checkbox - needed for multiple selection // radio - needed for single selection $optionChoicesItems.prop( 'type', choicesType ); // Dynamic Choices doesn't have default choices (selected options) - make all as unselected. if ( isDynamicChoices ) { $primary.find( 'option:selected' ).prop( 'selected', false ); } // Gets default choices. const selectedChoices = $optionChoicesItems.filter( ':checked' ); if ( ! isMultiple && selectedChoices.length ) { // Uncheck all choices. $optionChoicesItems.prop( 'checked', false ); // For single selection, we can choose only one. $( selectedChoices.get( 0 ) ).prop( 'checked', true ); } // Toggle selection for a placeholder option based on a select type. if ( $placeholder.length ) { $placeholder.prop( 'selected', ! isMultiple ); } // Update a primary field. app.dropdownField.helpers.update( fieldId, isDynamicChoices ); }, /** * Apply a style to . * * @since 1.6.1 * * @param {string} fieldId Field ID. */ convertModernToClassic: ( fieldId ) => { const $primary = app.dropdownField.helpers.getPrimarySelector( fieldId ), isDynamicChoices = app.dropdownField.helpers.isDynamicChoices( fieldId ), instance = app.dropdownField.helpers.getInstance( $primary ), $sidebarChoices = $( '#wpforms-field-option-row-' + fieldId + '-choices' ), $sidebarList = $sidebarChoices.find( '.choices-list' ), elementsCount = $sidebarList.find( 'li' ).length; if ( instance && typeof instance.destroy === 'function' ) { // Destroy the instance of Choices.js. instance.destroy(); // Update a placeholder. app.dropdownField.helpers.updatePlaceholderChoice( instance, fieldId ); } // Update choices. if ( ! isDynamicChoices ) { app.fieldChoiceUpdate( 'select', fieldId, elementsCount ); } }, /** * Get initial choices. * * @since 1.8.2 * * @param {string} fieldId Field ID. * * @return {Object} Choices. */ getInitialChoices( fieldId ) { const $primary = app.dropdownField.helpers.getPrimarySelector( fieldId ), instance = app.dropdownField.helpers.getInstance( $primary ); return instance.config.choices; }, /** * Convert a Classic to Modern style selector. * * @since 1.6.1 * * @param {string} fieldId Field ID. */ convertClassicToModern( fieldId ) { const $primary = app.dropdownField.helpers.getPrimarySelector( fieldId ), isDynamicChoices = app.dropdownField.helpers.isDynamicChoices( fieldId ); // Update choices. if ( ! isDynamicChoices ) { app.fieldChoiceUpdate( 'select', fieldId ); } // Call a Choices.js initialization. app.dropdownField.events.choicesInit( $primary ); }, /** * Update a primary field. * * @since 1.6.1 * * @param {string} fieldId Field ID. * @param {boolean} isDynamicChoices True if `Dynamic Choices` is turned on. */ update( fieldId, isDynamicChoices ) { const $primary = app.dropdownField.helpers.getPrimarySelector( fieldId ); if ( app.dropdownField.helpers.isModernSelect( $primary ) ) { // If we had a `Modern` select before, then we need to make re-init - destroy() + init(). app.dropdownField.helpers.convertModernToClassic( fieldId ); if ( ! isDynamicChoices ) { app.dropdownField.events.choicesInit( $primary ); } } else if ( ! isDynamicChoices ) { // Update choices. app.fieldChoiceUpdate( 'select', fieldId ); } }, /** * Add a new choice to behave like a placeholder. * * @since 1.6.1 * * @param {Object} $jquerySelector jQuery primary selector. * @param {Object} instance The instance of Choices.js. * * @return {boolean} False if a fake placeholder wasn't added. */ addPlaceholderChoice( $jquerySelector, instance ) { // eslint-disable-line complexity const wpFormsField = $jquerySelector.closest( '.wpforms-field' ); if ( wpFormsField.length <= 0 ) { return false; } const fieldId = wpFormsField.data().fieldId; let hasDefaults = app.dropdownField.helpers.hasDefaults( fieldId ); if ( app.dropdownField.helpers.isDynamicChoices( fieldId ) ) { hasDefaults = false; } // Already has a placeholder. if ( false !== app.dropdownField.helpers.searchPlaceholderChoice( instance ) ) { return false; } // No choices. if ( ! instance.config.choices.length ) { return false; } const placeholder = wpf.decodeAllowedHTMLEntities( instance.config.choices[ 0 ].label ), isMultiple = $( instance.passedElement.element ).prop( 'multiple' ), selected = ! ( isMultiple || hasDefaults ); // Add a new choice as a placeholder. instance.setChoices( [ { value: '', label: placeholder, selected, placeholder: true }, ], 'value', 'label', false ); // Additional case for multiple select. if ( isMultiple ) { $( instance.input.element ).prop( 'placeholder', placeholder ); } return true; }, /** * Search a choice-placeholder item. * * @since 1.6.1 * * @param {Object} instance The instance of Choices.js. * * @return {boolean|object} False if a field doesn't have a choice-placeholder. * Otherwise - return choice item. */ searchPlaceholderChoice( instance ) { let find = false; instance.config.choices.forEach( function( item, i, choices ) { // eslint-disable-line no-unused-vars if ( 'undefined' !== typeof item.placeholder && true === item.placeholder ) { find = { key: i, item, }; return false; } } ); return find; }, /** * Add/update a placeholder. * * @since 1.6.1 * * @param {Object} instance The instance of Choices.js. * @param {string} fieldId Field ID. */ updatePlaceholderChoice( instance, fieldId ) { const $primary = $( instance.passedElement.element ), placeholderValue = wpf.sanitizeHTML( $( '#wpforms-field-option-' + fieldId + '-placeholder' ).val() ), placeholderChoice = app.dropdownField.helpers.searchPlaceholderChoice( instance ); let $placeholderOption = {}; // Get an option with placeholder. // Note: `.placeholder` class is skipped when calling Choices.js destroy() method. if ( 'object' === typeof placeholderChoice ) { $placeholderOption = $( $primary.find( 'option' ).get( placeholderChoice.key ) ); } // We have a placeholder and need to update the UI with it. if ( '' !== placeholderValue ) { if ( ! $.isEmptyObject( $placeholderOption ) && $placeholderOption.length ) { // Update a placeholder option. $placeholderOption .addClass( 'placeholder' ) .text( placeholderValue ); } else { // Add a placeholder option. $primary.prepend( '' ); } } else if ( $placeholderOption.length ) { // Remove the placeholder as it's empty. $placeholderOption.remove(); } }, /** * Is it a `Modern` style dropdown field? * * @since 1.6.1 * * @param {Object} $jquerySelector jQuery primary selector. * * @return {boolean} True if it's a `Modern` style select, false otherwise. */ isModernSelect( $jquerySelector ) { const instance = app.dropdownField.helpers.getInstance( $jquerySelector ); if ( 'object' !== typeof instance ) { return false; } if ( $.isEmptyObject( instance ) ) { return false; } return instance.initialised; }, /** * Save an instance of Choices.js. * * @since 1.6.1 * * @param {Object} $jquerySelector jQuery primary selector. * @param {Object} instance The instance of Choices.js. */ setInstance( $jquerySelector, instance ) { $jquerySelector.data( 'choicesjs', instance ); }, /** * Retrieve an instance of Choices.js. * * @since 1.6.1 * * @param {Object} $jquerySelector jQuery primary selector. * * @return {Object} The instance of Choices.js. */ getInstance( $jquerySelector ) { return $jquerySelector.data( 'choicesjs' ); }, /** * Get Dynamic Choices option field. * * @since 1.8.2 * * @param {string|number} fieldId Field ID. * * @return {HTMLElement|boolean} False if a field doesn't have a `Dynamic Choices` option. * Otherwise - return option field. */ getDynamicChoicesOption( fieldId ) { const $fieldOption = $( '#wpforms-field-option-' + fieldId + '-dynamic_choices' ); if ( ! $fieldOption.length ) { return false; } return $fieldOption; }, /** * Is `Dynamic Choices` used? * * @since 1.6.1 * * @param {string|number} fieldId Field ID. * * @return {boolean} True if a `Dynamic Choices` active, false otherwise. */ isDynamicChoices( fieldId ) { const $fieldOption = app.dropdownField.helpers.getDynamicChoicesOption( fieldId ); if ( ! $fieldOption.length ) { return false; } return '' !== $fieldOption.val(); }, /** * Is `Dynamic Choices` option type is `Modern`? * * @since 1.8.2 * * @param {string|number} fieldId Field ID. * @return {boolean} True if a `Dynamic Choices` option type is `Modern`, false otherwise. */ isDynamicChoicesOptionModern( fieldId ) { const $fieldOption = $( '#wpforms-field-option-' + fieldId + '-style' ); if ( ! $fieldOption.length ) { return false; } return $fieldOption.val() === 'modern'; }, /** * Get a Dynamic Choices option type. * * @since 1.8.2 * * @param {string|number} fieldId Field ID. * * @return {string|boolean} False if a field doesn't have a `Dynamic Choices` option. * Otherwise - return option type. */ getDynamicChoicesOptionType( fieldId ) { const $fieldOption = app.dropdownField.helpers.getDynamicChoicesOption( fieldId ); if ( ! $fieldOption.length ) { return false; } return $fieldOption.val(); }, /** * Get a Dynamic Choices option source. * * @since 1.8.2 * * @param {string|number} fieldId Field ID. * * @return {string|boolean} False if a field doesn't have a `Dynamic Choices` option. * Otherwise - return option source. */ getDynamicChoicesOptionSource( fieldId ) { const type = app.dropdownField.helpers.getDynamicChoicesOptionType( fieldId ); const $fieldOption = $( '#wpforms-field-option-' + fieldId + '-dynamic_' + type ); if ( ! $fieldOption.length ) { return false; } return $fieldOption.find( 'option:selected' ).text(); }, /** * Is a field having default choices? * * @since 1.6.1 * * @param {string} fieldId Field ID. * * @return {boolean} True if a field has default choices. */ hasDefaults( fieldId ) { const $choicesList = $( '#wpforms-field-option-row-' + fieldId + '-choices .choices-list' ); return !! $choicesList.find( 'input.default:checked' ).length; }, /** * Retrieve a jQuery selector for the Primary field. * * @since 1.6.1 * * @param {string} fieldId Field ID. * * @return {Object} jQuery primary selector. */ getPrimarySelector( fieldId ) { return $( '#wpforms-field-' + fieldId + ' .primary-input' ); }, }, }, /** * Add number slider events listeners. * * @since 1.5.7 * * @param {Object} $builder JQuery object. */ numberSliderEvents( $builder ) { // Minimum update. $builder.on( 'focusout', '.wpforms-field-option-row-min_max .wpforms-input-row .wpforms-number-slider-min', app.fieldNumberSliderUpdateMin ); // Maximum update. $builder.on( 'focusout', '.wpforms-field-option-row-min_max .wpforms-input-row .wpforms-number-slider-max', app.fieldNumberSliderUpdateMax ); // Change default input value. $builder.on( 'input', '.wpforms-number-slider-default-value', _.debounce( app.changeNumberSliderDefaultValue, 500 ) ); // Change default input value if it's empty. $builder.on( 'focusout', '.wpforms-number-slider-default-value', app.changeNumberSliderEmptyDefaultValue ); // Trigger input event on default value input to check if it's valid. $builder.find( '.wpforms-number-slider-default-value' ).trigger( 'input' ); // Change step value. $builder.on( 'input', '.wpforms-number-slider-step', _.debounce( app.changeNumberSliderStep, 500 ) ); // Check step value. $builder.on( 'focusout', '.wpforms-number-slider-step', app.checkNumberSliderStep ); // Change value display. $builder.on( 'input', '.wpforms-number-slider-value-display', _.debounce( app.changeNumberSliderValueDisplay, 500 ) ); // Change min value. $builder.on( 'input', '.wpforms-number-slider-min', _.debounce( app.changeNumberSliderMin, 500 ) ); // Change max value. $builder.on( 'input', '.wpforms-number-slider-max', _.debounce( app.changeNumberSliderMax, 500 ) ); }, /** * Change number slider min option. * * @since 1.5.7 * * @param {Object} event Input event. */ changeNumberSliderMin( event ) { const value = parseFloat( event.target.value ); if ( isNaN( value ) ) { return; } const fieldID = $( event.target ).parents( '.wpforms-field-option-row' ).data( 'fieldId' ); app.updateNumberSliderDefaultValueAttr( fieldID, event.target.value, 'min' ); }, /** * Change number slider max option. * * @since 1.5.7 * * @param {Object} event Input event. */ changeNumberSliderMax( event ) { const value = parseFloat( event.target.value ); if ( isNaN( value ) ) { return; } const fieldID = $( event.target ).parents( '.wpforms-field-option-row' ).data( 'fieldId' ); app.updateNumberSliderDefaultValueAttr( fieldID, event.target.value, 'max' ) .updateNumberSliderStepValueMaxAttr( fieldID, event.target.value ); }, /** * Change number slider value display option. * * @since 1.5.7 * * @param {Object} event Input event. */ changeNumberSliderValueDisplay( event ) { const str = event.target.value; const fieldID = $( event.target ).parents( '.wpforms-field-option-row' ).data( 'fieldId' ); const defaultValue = document.getElementById( 'wpforms-field-option-' + fieldID + '-default_value' ); if ( defaultValue ) { app.updateNumberSliderHintStr( fieldID, str ) .updateNumberSliderHint( fieldID, defaultValue.value ); } }, /** * Change number slider step option. * * @since 1.5.7 * * @param {Object} event Input event. */ changeNumberSliderStep( event ) { const $el = $( this ); const value = parseFloat( $el.val() ); if ( isNaN( value ) ) { return; } if ( value <= 0 ) { return; } const $options = $( $el ).closest( '.wpforms-field-option' ); const max = parseFloat( $options.find( '.wpforms-number-slider-max' ).val() ); const min = parseFloat( $options.find( '.wpforms-number-slider-min' ).val() ); const maxStep = ( max - min ).toFixed( 2 ); if ( value > maxStep ) { event.target.value = maxStep; $el.trigger( 'input' ); return; } const fieldID = $( event.target ).parents( '.wpforms-field-option-row' ).data( 'fieldId' ); const defaultValue = $( '#wpforms-field-option-' + fieldID + '-default_value' ).val(); app.checkMultiplicitySliderDefaultValue( fieldID, defaultValue, value, min ) .updateNumberSliderAttr( fieldID, value, 'step' ) .updateNumberSliderDefaultValueAttr( fieldID, value, 'step' ); }, /** * Check multiplicity of a slider default value. * * @since 1.8.4 * * @param {string} fieldId Field ID. * @param {number} value Default value. * @param {number} step Step value. * @param {number} min Min value. * * @return {Object} App instance. */ checkMultiplicitySliderDefaultValue( fieldId, value, step, min ) { const $printSelector = $( `#wpforms-field-option-row-${ fieldId }-default_value` ); value = parseFloat( value ); if ( value % step === 0 ) { app.removeNotice( $printSelector ); return this; } const closestSmallerMultiple = min + ( Math.floor( ( value - min ) / step ) * step ); const closestLargerMultiple = min + ( Math.ceil( ( value - min ) / step ) * step ); const formatNumber = ( num ) => ( num % 1 === 0 ? num.toString() : num.toFixed( 2 ) ); const normalizedValue = formatNumber( value ); const normalizedSmaller = formatNumber( closestSmallerMultiple ); const normalizedLarger = formatNumber( closestLargerMultiple ); if ( normalizedSmaller === normalizedLarger || normalizedSmaller === normalizedValue || normalizedLarger === normalizedValue ) { app.removeNotice( $printSelector ); return this; } const updatedMessage = wpforms_builder.number_slider_error_valid_default_value .replace( '{from}', normalizedSmaller ) .replace( '{to}', normalizedLarger ); app.printNotice( updatedMessage, $printSelector ); return this; }, /** * Print a notice. * * @since 1.8.4 * * @param {string} message Message to print. * @param {Object} $printSelector jQuery element selector. */ printNotice( message, $printSelector ) { if ( $printSelector.length ) { this.removeNotice( $printSelector ); $printSelector.append( `

    ${ message }

    ` ); } }, /** * Remove a notice. * * @since 1.8.4 * * @param {Object} $printSelector jQuery element selector. */ removeNotice( $printSelector ) { if ( $printSelector.length && $printSelector.find( '.wpforms-alert' ).length ) { $printSelector.find( '.wpforms-alert' ).remove(); } }, /** * Check the number slider step option. * * @since 1.6.2.3 * * @param {Object} event Focusout event object. */ checkNumberSliderStep( event ) { const value = parseFloat( event.target.value ); if ( ! isNaN( value ) && value > 0 ) { return; } const $input = $( this ); $.confirm( { title: wpforms_builder.heads_up, content: wpforms_builder.error_number_slider_increment, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action() { $input.val( '' ).trigger( 'focus' ); }, }, }, } ); }, /** * Update number slider default value if it's empty. * * @since 1.9.3 * * @param {Object} event Input event. */ changeNumberSliderEmptyDefaultValue( event ) { const value = parseFloat( event.target.value ); if ( isNaN( value ) ) { const newValue = parseFloat( event.target.min ); event.target.value = newValue; const step = parseFloat( event.target.step ); const min = parseFloat( event.target.min ); const fieldID = $( event.target ).parents( '.wpforms-field-option-row-default_value' ).data( 'fieldId' ); app.checkMultiplicitySliderDefaultValue( fieldID, newValue, step, min ) .updateNumberSlider( fieldID, newValue ) .updateNumberSliderHint( fieldID, newValue ); } }, /** * Change number slider default value option. * * @since 1.5.7 * * @param {Object} event Input event. */ changeNumberSliderDefaultValue( event ) { const value = parseFloat( event.target.value ); if ( ! isNaN( value ) ) { const max = parseFloat( event.target.max ); if ( value > max ) { event.target.value = max; return; } const min = parseFloat( event.target.min ); if ( value < min ) { event.target.value = min; return; } const step = parseFloat( event.target.step ); const fieldID = $( event.target ).parents( '.wpforms-field-option-row-default_value' ).data( 'fieldId' ); app.checkMultiplicitySliderDefaultValue( fieldID, value, step, min ) .updateNumberSlider( fieldID, value ) .updateNumberSliderHint( fieldID, value ); } }, /** * Update number slider default value attribute. * * @since 1.5.7 * * @param {number} fieldID Field ID. * @param {*} newValue Default value attribute. * @param {*} attr Attribute name. * * @return {Object} App instance. */ updateNumberSliderDefaultValueAttr( fieldID, newValue, attr ) { const input = document.getElementById( 'wpforms-field-option-' + fieldID + '-default_value' ); if ( input ) { const value = parseFloat( input.value ); input.setAttribute( attr, newValue ); newValue = parseFloat( newValue ); if ( 'max' === attr && value > newValue ) { input.value = newValue; } if ( 'min' === attr && value < newValue ) { input.value = newValue; } } return this; }, /** * Update number slider value. * * @since 1.5.7 * * @param {number} fieldID Field ID. * @param {string} value Number slider value. * * @return {Object} App instance. */ updateNumberSlider( fieldID, value ) { const numberSlider = document.getElementById( 'wpforms-number-slider-' + fieldID ); if ( numberSlider ) { numberSlider.value = value; } return this; }, /** * Update number slider attribute. * * @since 1.5.7 * * @param {number} fieldID Field ID. * @param {any} value Attribute value. * @param {*} attr Attribute name. * * @return {Object} App instance. */ updateNumberSliderAttr( fieldID, value, attr ) { const numberSlider = document.getElementById( 'wpforms-number-slider-' + fieldID ); if ( numberSlider ) { numberSlider.setAttribute( attr, value ); } return this; }, /** * Update number slider hint string. * * @since 1.5.7 * * @param {number} fieldID Field ID. * @param {string} str Hint string. * * @return {Object} App instance. */ updateNumberSliderHintStr( fieldID, str ) { const hint = document.getElementById( 'wpforms-number-slider-hint-' + fieldID ); if ( hint ) { hint.dataset.hint = str; } return this; }, /** * Update number slider Hint value. * * @since 1.5.7 * * @param {number} fieldID Field ID. * @param {string} value Hint value. * * @return {Object} App instance. */ updateNumberSliderHint( fieldID, value ) { const hint = document.getElementById( 'wpforms-number-slider-hint-' + fieldID ); if ( hint ) { hint.innerHTML = wpf.sanitizeHTML( hint.dataset.hint ).replaceAll( '{value}', '' + value + '' ); } return this; }, /** * Update min attribute. * * @since 1.5.7 * * @param {Object} event Input event. */ fieldNumberSliderUpdateMin( event ) { const current = parseFloat( event.target.value ); if ( isNaN( current ) ) { return; } const $options = $( event.target ).parents( '.wpforms-field-option-row-min_max' ); const max = parseFloat( $options.find( '.wpforms-number-slider-max' ).val() ); if ( max <= current ) { event.preventDefault(); this.value = max; return; } const fieldId = $options.data( 'field-id' ); const numberSlider = $builder.find( '#wpforms-field-' + fieldId + ' input[type="range"]' ); numberSlider.attr( 'min', current ); }, /** * Update max attribute. * * @since 1.5.7 * * @param {Object} event Input event. */ fieldNumberSliderUpdateMax( event ) { const current = parseFloat( event.target.value ); if ( isNaN( current ) ) { return; } const $options = $( event.target ).parents( '.wpforms-field-option-row-min_max' ); const min = parseFloat( $options.find( '.wpforms-number-slider-min' ).val() ); if ( min >= current ) { event.preventDefault(); this.value = min; return; } const fieldId = $options.data( 'field-id' ); const numberSlider = $builder.find( '#wpforms-field-' + fieldId + ' input[type="range"]' ); numberSlider.attr( 'max', current ); }, /** * Update max attribute for step value. * * @since 1.5.7 * * @param {number} fieldID Field ID. * @param {*} newValue Default value attribute. * * @return {Object} App instance. */ updateNumberSliderStepValueMaxAttr( fieldID, newValue ) { const input = document.getElementById( 'wpforms-field-option-' + fieldID + '-step' ); if ( input ) { const value = parseFloat( input.value ); input.setAttribute( 'max', newValue ); newValue = parseFloat( newValue ); if ( value > newValue ) { input.value = newValue; $( input ).trigger( 'input' ); } } return this; }, /** * Update upload selector. * * @since 1.5.6 * * @param {Object} target Changed :input. */ fieldFileUploadPreviewUpdate( target ) { const $options = $( target ).parents( '.wpforms-field-option-file-upload' ); const fieldId = $options.data( 'field-id' ); const styleOption = $options.find( '#wpforms-field-option-' + fieldId + '-style' ).val(); const $maxFileNumberRow = $options.find( '#wpforms-field-option-row-' + fieldId + '-max_file_number' ); const maxFileNumber = parseInt( $maxFileNumberRow.find( 'input' ).val(), 10 ); const $preview = $( '#wpforms-field-' + fieldId ); const classicPreview = '.wpforms-file-upload-builder-classic'; const modernPreview = '.wpforms-file-upload-builder-modern'; if ( styleOption === 'classic' ) { $( classicPreview, $preview ).removeClass( 'wpforms-hide' ); $( modernPreview, $preview ).addClass( 'wpforms-hide' ); $maxFileNumberRow.addClass( 'wpforms-hidden' ); } else { // Change hint and title. if ( maxFileNumber > 1 ) { $preview .find( '.modern-title' ) .text( wpforms_builder.file_upload.preview_title_plural ); $preview .find( '.modern-hint' ) .text( wpforms_builder.file_upload.preview_hint.replace( '{maxFileNumber}', maxFileNumber ) ) .removeClass( 'wpforms-hide' ); } else { $preview .find( '.modern-title' ) .text( wpforms_builder.file_upload.preview_title_single ); $preview .find( '.modern-hint' ) .text( wpforms_builder.file_upload.preview_hint.replace( '{maxFileNumber}', 1 ) ) .addClass( 'wpforms-hide' ); } // Display the preview. $( classicPreview, $preview ).addClass( 'wpforms-hide' ); $( modernPreview, $preview ).removeClass( 'wpforms-hide' ); $maxFileNumberRow.removeClass( 'wpforms-hidden' ); } }, /** * Update limit controls by changing checkbox. * * @since 1.5.6 * * @param {number} id Field id. * @param {boolean} checked Whether an option is checked or not. */ updateTextFieldsLimitControls( id, checked ) { if ( ! checked ) { $( '#wpforms-field-option-row-' + id + '-limit_controls' ).addClass( 'wpforms-hide' ); } else { $( '#wpforms-field-option-row-' + id + '-limit_controls' ).removeClass( 'wpforms-hide' ); } }, /** * Update disabling today's date controls by changing checkbox. * * @since 1.8.9.4 * * @param {number} id Field id. * @param {boolean} checked Whether an option is checked or not. */ updateDisableTodaysDateControls( id, checked ) { $( `#wpforms-field-option-row-${ id }-date_disable_todays_date` ) .toggleClass( 'wpforms-hide', ! checked ); }, /** * Update Password Strength controls by changing checkbox. * * @since 1.6.7 * * @param {number} id Field id. * @param {boolean} checked Whether an option is checked or not. */ updatePasswordStrengthControls( id, checked ) { const $strengthControls = $( '#wpforms-field-option-row-' + id + '-password-strength-level' ); if ( checked ) { $strengthControls.removeClass( 'wpforms-hidden' ); } else { $strengthControls.addClass( 'wpforms-hidden' ); } }, /** * Update Rich Text media controls by changing checkbox. * * @since 1.7.0 */ updateRichTextMediaFieldsLimitControls() { const $this = $( this ), fieldId = $this.closest( '.wpforms-field-option-row-media_enabled' ).data( 'field-id' ), $mediaControls = $( '#wpforms-field-option-row-' + fieldId + '-media_controls' ), $toolbar = $( '#wpforms-field-' + fieldId + ' .wpforms-richtext-wrap .mce-toolbar-grp' ); if ( ! $this.is( ':checked' ) ) { $mediaControls.hide(); $toolbar.removeClass( 'wpforms-field-richtext-media-enabled' ); } else { $mediaControls.show(); $toolbar.addClass( 'wpforms-field-richtext-media-enabled' ); } }, /** * Update Rich Text style preview by changing select. * * @since 1.7.0 */ updateRichTextStylePreview() { const $this = $( this ), fieldId = $this.closest( '.wpforms-field-option-row-style' ).data( 'field-id' ), $toolbar = $( '#wpforms-field-' + fieldId + ' .wpforms-richtext-wrap .mce-toolbar-grp' ); $toolbar.toggleClass( 'wpforms-field-richtext-toolbar-basic', $this.val() !== 'full' ); }, /** * Element bindings. * * @since 1.0.0 */ bindUIActions() { // General Panels. app.bindUIActionsPanels(); // Fields Panel. app.bindUIActionsFields(); // Settings Panel. app.bindUIActionsSettings(); // Revisions Panel. app.bindUIActionsRevisions(); // Save and Exit. app.bindUIActionsSaveExit(); // General/ global. app.bindUIActionsGeneral(); // Preview actions. app.bindUIActionsPreview(); }, /** * Bind UI actions for the preview tab. * * @since 1.9.4 */ bindUIActionsPreview() { // Open preview tab or focus on it if it's already opened. elements.$previewButton.on( 'click', function( e ) { e.preventDefault(); const previewUrl = $( this ).attr( 'href' ); if ( previewTab && ! previewTab.closed && previewTab.location.href.includes( 'wpforms_form_preview' ) ) { previewTab.focus(); } else { previewTab = window.open( previewUrl, '_blank' ); } } ); // Reload preview tab after saving the form. $builder.on( 'wpformsSaved', function() { if ( previewTab && ! previewTab.closed && previewTab.location.href.includes( 'wpforms_form_preview' ) ) { previewTab.location.reload(); } } ); }, //--------------------------------------------------------------------// // General Panels //--------------------------------------------------------------------// /** * Element bindings for general panel tasks. * * @since 1.0.0 */ bindUIActionsPanels() { // Panel switching. $builder.on( 'click', '#wpforms-panels-toggle button, .wpforms-panel-switch', function( e ) { e.preventDefault(); app.panelSwitch( $( this ).data( 'panel' ) ); } ); // Panel sections switching. $builder.on( 'click', '.wpforms-panel .wpforms-panel-sidebar-section', function( e ) { app.panelSectionSwitch( this, e ); } ); // Panel sidebar toggle. $builder.on( 'click', '.wpforms-panels .wpforms-panel-sidebar-content .wpforms-panel-sidebar-toggle', function() { $( this ).parent().toggleClass( 'wpforms-panel-sidebar-closed' ); } ); }, /** * Switch Panels. * * @since 1.0.0 * @since 1.5.9 Added `wpformsPanelSwitched` trigger. * * @param {string} panel Panel slug. * * @return {void|boolean} Void or false. */ panelSwitch( panel ) { const $panel = $( '#wpforms-panel-' + panel ); if ( ! $panel.hasClass( 'active' ) ) { const event = WPFormsUtils.triggerEvent( $builder, 'wpformsPanelSwitch', [ panel ] ); // Allow callbacks on `wpformsPanelSwitch` to cancel panel switching by triggering `event.preventDefault()`. if ( event.isDefaultPrevented() || ! wpforms_panel_switch ) { return false; } $( '#wpforms-panels-toggle' ).find( 'button' ).removeClass( 'active' ); $( '.wpforms-panel' ).removeClass( 'active' ); $( '.wpforms-panel-' + panel + '-button' ).addClass( 'active' ); $panel.addClass( 'active' ); history.replaceState( {}, null, wpf.updateQueryString( 'view', panel ) ); // Update the active section parameter in the URL. let section; const activeSectionElement = $panel.find( '.active' ); if ( activeSectionElement.length && activeSectionElement.data( 'section' ) !== 'default' ) { section = activeSectionElement.data( 'section' ); } history.replaceState( {}, null, wpf.updateQueryString( 'section', section ) ); $builder.trigger( 'wpformsPanelSwitched', [ panel ] ); } }, /** * Switch Panel section. * * @since 1.0.0 * * @param {Element} el Element. * @param {Event} e Event. * * @return {boolean|void} False when not switched. */ panelSectionSwitch( el, e ) { // eslint-disable-line complexity if ( e ) { e.preventDefault(); } const $this = $( el ); if ( $this.hasClass( 'upgrade-modal' ) || $this.hasClass( 'education-modal' ) ) { return; } const $panel = $this.parent().parent(), section = $this.data( 'section' ), $sectionButton = $panel.find( `.wpforms-panel-sidebar-section[data-section="${ section }"]` ); if ( ! $sectionButton.hasClass( 'active' ) ) { const event = WPFormsUtils.triggerEvent( $builder, 'wpformsPanelSectionSwitch', section ); // Allow callbacks on `wpformsPanelSectionSwitch` to cancel panel section switching by triggering `event.preventDefault()`. if ( event.isDefaultPrevented() || ! wpforms_panel_switch ) { return false; } const $sectionButtons = $panel.find( '.wpforms-panel-sidebar-section' ); $sectionButtons.removeClass( 'active' ); $sectionButton.addClass( 'active' ); $panel.find( '.wpforms-panel-content-section' ).hide(); $panel.find( '.wpforms-panel-content-section-' + section ).show(); // Update the active section parameter in the URL. history.replaceState( {}, null, wpf.updateQueryString( 'section', section ) ); } }, //--------------------------------------------------------------------// // Setup Panel //--------------------------------------------------------------------// /** * Element bindings for a Setup panel. * * @since 1.0.0 * @since 1.6.8 Deprecated. * * @deprecated Use `WPForms.Admin.Builder.Setup.events()` instead. */ bindUIActionsSetup() { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "WPFormsBuilder.bindUIActionsSetup()" has been deprecated, please use the new "WPForms.Admin.Builder.Setup.events()" function instead!' ); WPForms.Admin.Builder.Setup.events(); }, /** * Select template. * * @since 1.0.0 * @since 1.6.8 Deprecated. * * @deprecated Use `WPForms.Admin.Builder.Setup.selectTemplate()` instead. * * @param {Object} el DOM element object. * @param {Object} e Event object. */ templateSelect( el, e ) { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "WPFormsBuilder.templateSelect()" has been deprecated, please use the new "WPForms.Admin.Builder.Setup.selectTemplate()" function instead!' ); WPForms.Admin.Builder.Setup.selectTemplate( e ); }, //--------------------------------------------------------------------// // Fields Panel //--------------------------------------------------------------------// /** * Element bindings for Fields panel. * * @since 1.0.0 */ bindUIActionsFields() { // eslint-disable-line max-lines-per-function // Switched to the Fields panel. $builder.on( 'wpformsPanelSwitched', function( e, panel ) { if ( panel !== 'fields' ) { return; } // Detect the case when the field Options tab is active, but there is no active field on the preview panel. if ( $( '#field-options a' ).hasClass( 'active' ) && $( '.wpforms-field-wrap .wpforms-field.active' ).length === 0 ) { app.fieldTabToggle( 'field-options' ); } } ); // Field sidebar tab toggle $builder.on( 'click', '.wpforms-tab a', function( e ) { e.preventDefault(); app.fieldTabToggle( $( this ).parent().attr( 'id' ) ); } ); // Field sidebar group toggle $builder.on( 'click', '.wpforms-add-fields-heading', function( e ) { e.preventDefault(); app.fieldGroupToggle( $( this ), 'click' ); } ); // Form field preview clicking. $builder.on( 'click', '.wpforms-field', function( event ) { if ( app.isFieldPreviewActionsDisabled( this ) ) { return; } // Allow clicking on the "dismiss" button inside the field. if ( event.target.classList.contains( 'wpforms-dismiss-button' ) ) { return; } // Dismiss the main context menu when it is open. if ( WPForms.Admin.Builder.ContextMenu ) { WPForms.Admin.Builder.ContextMenu.hideMainContextMenu( event ); } event.stopPropagation(); app.fieldTabToggle( $( this ).data( 'field-id' ) ); } ); // Prevent interactions with inputs on the preview panel. $builder.on( 'mousedown click', '.wpforms-field input, .wpforms-field select, .wpforms-field textarea', function( e ) { e.preventDefault(); this.blur(); } ); // Field delete. $builder.on( 'click', '.wpforms-field-delete', function( e ) { e.preventDefault(); e.stopPropagation(); if ( app.isFormPreviewActionsDisabled( this ) ) { return; } if ( WPForms.Admin.Builder.ContextMenu ) { WPForms.Admin.Builder.ContextMenu.hideMenu(); } app.fieldDelete( $( this ).parent().data( 'field-id' ) ); } ); // Field duplicate. $builder.on( 'click', '.wpforms-field-duplicate', function( e ) { e.preventDefault(); e.stopPropagation(); if ( app.isFormPreviewActionsDisabled( this ) ) { return; } if ( WPForms.Admin.Builder.ContextMenu ) { WPForms.Admin.Builder.ContextMenu.hideMenu(); } app.fieldDuplicate( $( this ).parent().data( 'field-id' ) ); } ); // Field add. $builder.on( 'click', '.wpforms-add-fields-button', function( e ) { e.preventDefault(); const $field = $( this ); if ( $field.hasClass( 'ui-draggable-disabled' ) ) { return; } const type = $field.data( 'field-type' ), event = WPFormsUtils.triggerEvent( $builder, 'wpformsBeforeFieldAddOnClick', [ type, $field ] ); // Allow callbacks on `wpformsBeforeFieldAddOnClick` to cancel adding field // by triggering `event.preventDefault()`. if ( event.isDefaultPrevented() ) { return; } app.fieldAdd( type, { $sortable: 'default' } ); } ); // New field choices should be sortable $builder.on( 'wpformsFieldAdd', function( event, id, type ) { const fieldTypes = [ 'select', 'radio', 'checkbox', 'payment-multiple', 'payment-checkbox', 'payment-select', ]; if ( $.inArray( type, fieldTypes ) !== -1 ) { app.fieldChoiceSortable( type, `#wpforms-field-option-row-${ id }-choices ul` ); } } ); // Field option tab toggle. $builder.on( 'wpformsFieldOptionTabToggle', function( e, fieldId ) { app.fieldLayoutSelectorInit( fieldId ); } ); // Field choice "Add new". $builder.on( 'click', '.wpforms-field-option-row-choices .add', function( e ) { app.fieldChoiceAdd( e, $( this ) ); } ); // Field choice "Delete". $builder.on( 'click', '.wpforms-field-option-row-choices .remove', function( e ) { app.fieldChoiceDelete( e, $( this ) ); } ); // Field choices' defaults, before change. $builder.on( 'mousedown', '.wpforms-field-option-row-choices input[type=radio]', function( e ) { // eslint-disable-line no-unused-vars const $this = $( this ); if ( $this.is( ':checked' ) ) { $this.attr( 'data-checked', '1' ); } else { $this.attr( 'data-checked', '0' ); } } ); // Field choices' defaults. $builder.on( 'click', '.wpforms-field-option-row-choices input[type=radio]', function( e ) { // eslint-disable-line no-unused-vars const $this = $( this ), list = $this.parent().parent(); $this.parent().parent().find( 'input[type=radio]' ).not( this ).prop( 'checked', false ); if ( $this.attr( 'data-checked' ) === '1' ) { $this.prop( 'checked', false ); $this.attr( 'data-checked', '0' ); } app.fieldChoiceUpdate( list.data( 'field-type' ), list.data( 'field-id' ), list.find( 'li' ).length ); } ); // Field choices update preview area. $builder.on( 'change', '.wpforms-field-option-row-choices input[type=checkbox]', function( e ) { // eslint-disable-line no-unused-vars const list = $( this ).parent().parent(); app.fieldChoiceUpdate( list.data( 'field-type' ), list.data( 'field-id' ), list.find( 'li' ).length ); } ); // Field choices display value toggle. $builder.on( 'change', '.wpforms-field-option-row-show_values input', function( e ) { // eslint-disable-line no-unused-vars $( this ).closest( '.wpforms-field-option' ).find( '.wpforms-field-option-row-choices ul' ).toggleClass( 'show-values' ); } ); // Field choices image toggle. $builder.on( 'change', '.wpforms-field-option-row-choices_images input', function() { const $this = $( this ), $optionRow = $this.closest( '.wpforms-field-option-row' ), fieldID = $optionRow.data( 'field-id' ), $fieldOptions = $( '#wpforms-field-option-' + fieldID ), checked = $this.is( ':checked' ), type = $fieldOptions.find( '.wpforms-field-option-hidden-type' ).val(), $iconToggle = $optionRow.siblings( '.wpforms-field-option-row-choices_icons' ).find( 'input' ); // Toggle icon choices off. if ( checked && $iconToggle.is( ':checked' ) ) { $iconToggle.prop( 'checked', false ).trigger( 'change' ); } $optionRow.find( '.wpforms-alert' ).toggleClass( 'wpforms-hidden' ); $fieldOptions.find( '.wpforms-field-option-row-choices ul' ).toggleClass( 'show-images' ); $fieldOptions.find( '.wpforms-field-option-row-choices_images_style' ).toggleClass( 'wpforms-hidden' ); $fieldOptions.find( '.wpforms-field-option-row-dynamic_choices' ).toggleClass( 'wpforms-hidden', checked ); if ( checked ) { $( '#wpforms-field-option-' + fieldID + '-input_columns' ).val( 'inline' ).trigger( 'change' ); } else { $( '#wpforms-field-option-' + fieldID + '-input_columns' ).val( '' ).trigger( 'change' ); } app.fieldChoiceUpdate( type, fieldID ); } ); // Field choices image upload add/remove image. $builder.on( 'wpformsImageUploadAdd wpformsImageUploadRemove', function( event, $this, $container ) { const $list = $container.closest( '.choices-list' ), fieldID = $list.data( 'field-id' ), type = $list.data( 'field-type' ); app.fieldChoiceUpdate( type, fieldID ); } ); // Field choices image style toggle. $builder.on( 'change', '.wpforms-field-option-row-choices_images_style select', function() { const fieldID = $( this ).parent().data( 'field-id' ), type = $( '#wpforms-field-option-' + fieldID ).find( '.wpforms-field-option-hidden-type' ).val(); app.fieldChoiceUpdate( type, fieldID ); } ); // Updates field choices text in almost real time. $builder.on( 'keyup', '.wpforms-field-option-row-choices input.label, .wpforms-field-option-row-choices input.value', function( e ) { // eslint-disable-line no-unused-vars const $list = $( this ).parent().parent(); app.fieldChoiceUpdate( $list.data( 'field-type' ), $list.data( 'field-id' ) ); } ); // Sanitize field choices text on focus out. $builder.on( 'focusout', '.wpforms-field-option-row-choices input.label, .wpforms-field-option-row-choices input.value', function( e ) { // eslint-disable-line no-unused-vars const input = $( this ); input.val( wpf.sanitizeHTML( input.val(), wpforms_builder.allowed_label_html_tags ) ); } ); // Field Choices Bulk Add $builder.on( 'click', '.toggle-bulk-add-display', function( e ) { e.preventDefault(); app.fieldChoiceBulkAddToggle( this ); } ); $builder.on( 'click', '.toggle-bulk-add-presets', function( e ) { e.preventDefault(); const $presetList = $( this ).closest( '.bulk-add-display' ).find( 'ul' ); if ( $presetList.css( 'display' ) === 'block' ) { $( this ).text( wpforms_builder.bulk_add_presets_show ); } else { $( this ).text( wpforms_builder.bulk_add_presets_hide ); } $presetList.stop().slideToggle(); } ); $builder.on( 'click', '.bulk-add-preset-insert', function( e ) { e.preventDefault(); const $this = $( this ), preset = $this.data( 'preset' ), $container = $this.closest( '.bulk-add-display' ), $presetList = $container.find( 'ul' ), $presetToggle = $container.find( '.toggle-bulk-add-presets' ), $textarea = $container.find( 'textarea' ); $textarea.val( '' ); $textarea.insertAtCaret( wpforms_preset_choices[ preset ].choices.join( '\n' ) ); $presetToggle.text( wpforms_builder.bulk_add_presets_show ); $presetList.slideUp(); } ); $builder.on( 'click', '.bulk-add-insert', function( e ) { e.preventDefault(); app.fieldChoiceBulkAddInsert( this ); } ); // Field Options group tabs. $builder.on( 'click', '.wpforms-field-option-group-toggle:not(.education-modal)', function( e ) { const event = WPFormsUtils.triggerEvent( $builder, 'wpformsFieldOptionGroupToggle' ); // Allow callbacks on `wpformsFieldOptionGroupToggle` to cancel tab toggle by triggering `event.preventDefault()`. if ( event.isDefaultPrevented() ) { return false; } e.preventDefault(); const $group = $( this ).closest( '.wpforms-field-option-group' ); $group.siblings( '.wpforms-field-option-group' ).removeClass( 'active' ); $group.addClass( 'active' ); $builder.trigger( 'wpformsFieldOptionGroupToggled', [ $group ] ); } ); // Display toggle for an Address field hide address line 2 option. $builder.on( 'change', '.wpforms-field-option-address input.wpforms-subfield-hide', function( e ) { // eslint-disable-line no-unused-vars const $optionRow = $( this ).closest( '.wpforms-field-option-row' ), id = $optionRow.data( 'field-id' ), subfield = $optionRow.data( 'subfield' ); $( '#wpforms-field-' + id ).find( '.wpforms-' + subfield ).toggleClass( 'wpforms-hide' ); } ); // Real-time updates for the "Label" field option. $builder.on( 'input', '.wpforms-field-option-row-label input, .wpforms-field-option-row-name input', function( e ) { // eslint-disable-line no-unused-vars const $this = $( this ), id = $this.parent().data( 'field-id' ), $preview = $( '#wpforms-field-' + id ), type = $preview.data( 'field-type' ); let value = $this.val(), showEmptyLabel = value.length === 0; // Do not modify the label of the HTML field. if ( type === 'html' ) { showEmptyLabel = false; } if ( showEmptyLabel ) { value = wpforms_builder.empty_label; } $preview.toggleClass( 'label_empty', showEmptyLabel ).find( '> .label-title .text' ).text( value ); } ); // Real-time updates for "Description" field option $builder.on( 'input', '.wpforms-field-option-row-description textarea', function() { const $this = $( this ), value = wpf.sanitizeHTML( $this.val() ), id = $this.parent().data( 'field-id' ), // IIF description is not following other fields structure and needs to be selected separately. $desc = $( `#wpforms-field-${ id } > .description, #wpforms-field-${ id } .wpforms-field-internal-information-row-description` ); app.updateDescription( $desc, value ); $this.trigger( 'wpformsDescriptionFieldUpdated', { id, descField: $desc, value } ); } ); // Real-time updates for "Required" field option $builder.on( 'change', '.wpforms-field-option-row-required input', function( e ) { // eslint-disable-line no-unused-vars const id = $( this ).closest( '.wpforms-field-option-row' ).data( 'field-id' ); $( '#wpforms-field-' + id ).toggleClass( 'required' ); } ); // Real-time updates for "Summary" field option $builder.on( 'change', '.wpforms-field-option-row-summary input', function() { const $this = $( this ), id = $this.closest( '.wpforms-field-option-row' ).data( 'field-id' ); $( `#wpforms-field-${ id }` ).toggleClass( 'wpforms-summary-enabled' ); $this.closest( '.wpforms-field-option-group-inner' ).find( '.wpforms-total-summary-alert' ).toggleClass( 'wpforms-hidden' ); } ); // Real-time updates for "Confirmation" field option $builder.on( 'change', '.wpforms-field-option-row-confirmation input', function() { const id = $( this ).closest( '.wpforms-field-option-row' ).data( 'field-id' ); $( '#wpforms-field-' + id ).find( '.wpforms-confirm' ).toggleClass( 'wpforms-confirm-enabled wpforms-confirm-disabled' ); $( '#wpforms-field-option-' + id ).toggleClass( 'wpforms-confirm-enabled wpforms-confirm-disabled' ); } ); // Real-time updates for "Filter" field option $builder.on( 'change', '.wpforms-field-option-row-filter_type select', function() { const id = $( this ).parent().data( 'field-id' ), $toggledField = $( '#wpforms-field-option-' + id ); if ( $( this ).val() ) { $toggledField.removeClass( 'wpforms-filter-allowlist' ); $toggledField.removeClass( 'wpforms-filter-denylist' ); $toggledField.addClass( 'wpforms-filter-' + $( this ).val() ); } else { $toggledField.removeClass( 'wpforms-filter-allowlist' ); $toggledField.removeClass( 'wpforms-filter-denylist' ); } } ); $builder.on( 'focusout', '.wpforms-field-option-row-allowlist textarea,.wpforms-field-option-row-denylist textarea', function() { const $currentField = $( this ); let $current = 'allow'; $currentField.next( '.wpforms-alert' ).remove(); if ( $currentField.val() === '' ) { return; } const $allowField = $( '.wpforms-field-option-row-allowlist textarea' ), $denyField = $( '.wpforms-field-option-row-denylist textarea' ); if ( $currentField.is( $denyField ) ) { $current = 'deny'; } $.get( wpforms_builder.ajax_url, { nonce: wpforms_builder.nonce, content: JSON.stringify( { allow: $allowField.val(), deny: $denyField.val(), current: $current, } ), action: 'wpforms_sanitize_restricted_rules', }, function( res ) { if ( res.success ) { $currentField.val( res.data.currentField ); const intersect = res.data.intersect; if ( intersect.length !== 0 ) { const content = '

    ' + wpforms_builder.allow_deny_lists_intersect + '

    ' + '

    ' + intersect + '

    '; $.alert( { title: wpforms_builder.heads_up, content, icon: 'fa fa-exclamation-circle', type: 'red', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); } const restricted = res.data.restricted || 0; if ( restricted ) { $currentField.after( '

    ' + wpforms_builder.restricted_rules + '

    ' ); } } } ); } ); // Save focusout target. $builder.on( 'focusout', elements.defaultEmailSelector, function() { elements.$focusOutTarget = $( this ); app.focusOutEvent(); } ); // Real-time updates for "Size" field option $builder.on( 'change', '.wpforms-field-option-row-size select', function( e ) { // eslint-disable-line no-unused-vars const $this = $( this ), value = $this.val(), id = $this.parent().data( 'field-id' ); $( '#wpforms-field-' + id ).removeClass( 'size-small size-medium size-large' ).addClass( 'size-' + value ); } ); // Real-time updates for "Placeholder" field option. $builder.on( 'input', '.wpforms-field-option-row-placeholder input', function() { // eslint-disable-line complexity const $this = $( this ), id = $this.parent().data( 'field-id' ), $preview = $( '#wpforms-field-' + id ), $primary = $preview.find( '.primary-input' ); let value = wpf.sanitizeHTML( $this.val() ); // Single Item Field - if placeholder is cleared, set it to "price" placeholder. if ( $preview.data( 'field-type' ) === 'payment-single' && value === '' ) { value = $( '#wpforms-field-option-' + id + '-price' ).prop( 'placeholder' ); } // Set the placeholder value for `input` fields. if ( ! $primary.is( 'select' ) ) { $primary.prop( 'placeholder', value ); return; } // Modern select style. if ( app.dropdownField.helpers.isModernSelect( $primary ) ) { const choiceInstance = app.dropdownField.helpers.getInstance( $primary ); // Additional case for multiple select. if ( $primary.prop( 'multiple' ) ) { $( choiceInstance.input.element ).prop( 'placeholder', value ); } else { choiceInstance.setChoiceByValue( '' ); $primary.closest( '.choices' ).find( '.choices__inner .choices__placeholder' ).text( value ); const isDynamicChoices = $( '#wpforms-field-option-' + id + '-dynamic_choices' ).val(); // We need to re-initialize modern dropdown to properly determine and update placeholder. app.dropdownField.helpers.update( id, isDynamicChoices ); } return; } const $placeholder = $primary.find( '.placeholder' ); // Classic select style. if ( ! value.length && $placeholder.length ) { $placeholder.remove(); } else { if ( $placeholder.length ) { $placeholder.text( value ); } else { $primary.prepend( '' ); } $primary.find( '.placeholder' ).prop( 'selected', ! $primary.prop( 'multiple' ) ); } } ); // Real-time updates for "Confirmation Placeholder" field option $builder.on( 'input', '.wpforms-field-option-row-confirmation_placeholder input', function( e ) { // eslint-disable-line no-unused-vars const $this = $( this ), value = $this.val(), id = $this.parent().data( 'field-id' ); $( '#wpforms-field-' + id ).find( '.secondary-input' ).attr( 'placeholder', value ); } ); // Real-time updates for Date/Time, and Name "Placeholder" field options $builder.on( 'input', '.wpforms-field-option .format-selected input.placeholder', function() { const $this = $( this ); const value = $this.val(); const $fieldOptionRow = $this.closest( '.wpforms-field-option-row' ); const id = $fieldOptionRow.data( 'field-id' ); const subfield = $fieldOptionRow.data( 'subfield' ); $( '#wpforms-field-' + id ).find( '.wpforms-' + subfield + ' input' ).attr( 'placeholder', value ); } ); // Real-time updates for Address field "Placeholder" field options. $builder.on( 'input', '.wpforms-field-option-address input.placeholder', function() { const $this = $( this ); const $fieldOptionRow = $this.closest( '.wpforms-field-option-row' ); const id = $fieldOptionRow.data( 'field-id' ); const subfield = $fieldOptionRow.data( 'subfield' ); const $fieldPreviews = $( '#wpforms-field-' + id + ' .wpforms-' + subfield ).find( 'input, select' ); const $default = $fieldOptionRow.find( '#wpforms-field-option-' + id + '-' + subfield + '_default' ); const defaultValue = $default.val(); const defaultText = $default.find( 'option:selected' ).text(); const placeholderValue = $this.val(); $fieldPreviews.each( function() { const $fieldPreview = $( this ); if ( $fieldPreview.is( 'select' ) ) { const $option = $fieldPreview.find( '.placeholder' ); const value = defaultValue === '' && placeholderValue !== '' ? placeholderValue : defaultText; $option.text( value ); return; } $fieldPreview.attr( 'placeholder', placeholderValue ); } ); } ); // Real-time updates for "Default" field option. $builder.on( 'input', '.wpforms-field-option-row-default_value input', function() { const $this = $( this ); const value = wpf.sanitizeHTML( $this.val() ); const id = $this.closest( '.wpforms-field-option-row' ).data( 'field-id' ); const $preview = $( '#wpforms-field-' + id + ' .primary-input' ); $preview.val( value ); } ); // Real-time updates for "Default" field option of the Name and Address fields. $builder.on( 'input', '.wpforms-field-options-column input.default', function() { const $this = $( this ); const value = wpf.sanitizeHTML( $this.val() ); const $fieldOptionRow = $this.closest( '.wpforms-field-option-row' ); const id = $fieldOptionRow.data( 'field-id' ); const subfield = $fieldOptionRow.data( 'subfield' ); const $fieldPreview = $( '#wpforms-field-' + id + ' .wpforms-' + subfield + ' input' ); $fieldPreview.val( value ); } ); // Real-time updates for "Default" select field option of the Address field. $builder.on( 'change', '.wpforms-field-option-address select.default', function() { const $this = $( this ); const value = $this.val(); const textValue = $this.find( 'option:selected' ).text(); const $fieldOptionRow = $this.closest( '.wpforms-field-option-row' ); const id = $fieldOptionRow.data( 'field-id' ); const subfield = $fieldOptionRow.data( 'subfield' ); const scheme = $( '#wpforms-field-option-' + id + '-scheme' ).val(); const $placeholder = $fieldOptionRow.find( '#wpforms-field-option-' + id + '-' + subfield + '_placeholder' ); const placeholderValue = $placeholder.val(); const $fieldPreview = $( '#wpforms-field-' + id + ' .wpforms-address-scheme-' + scheme + ' .wpforms-' + subfield + ' .placeholder' ); value === '' && placeholderValue.trim().length > 0 ? $fieldPreview.text( placeholderValue ) : $fieldPreview.text( textValue ); } ); // Real-time updates for "Confirmation Placeholder" field option $builder.on( 'input', '.wpforms-field-option-row-confirmation_placeholder input', function( e ) { // eslint-disable-line no-unused-vars const $this = $( this ), value = $this.val(), id = $this.parent().data( 'field-id' ); $( '#wpforms-field-' + id ).find( '.secondary-input' ).attr( 'placeholder', value ); } ); // Real-time updates for "Hide Label" field option. $builder.on( 'change', '.wpforms-field-option-row-label_hide input', function( e ) { // eslint-disable-line no-unused-vars const id = $( this ).closest( '.wpforms-field-option-row' ).data( 'field-id' ); $( '#wpforms-field-' + id ).toggleClass( 'label_hide' ); } ); // Real-time updates for a Sub Label visibility field option. $builder.on( 'change', '.wpforms-field-option-row-sublabel_hide input', function( e ) { // eslint-disable-line no-unused-vars const id = $( this ).closest( '.wpforms-field-option-row' ).data( 'field-id' ); $( '#wpforms-field-' + id ).toggleClass( 'sublabel_hide' ); } ); // Real-time updates for a Quantity visibility field option. $builder.on( 'change', '.wpforms-field-option-row-enable_quantity input', function() { const id = $( this ).closest( '.wpforms-field-option-row' ).data( 'field-id' ), $preview = $( `#wpforms-field-${ id }` ); $( `#wpforms-field-option-row-${ id }-quantity` ).toggleClass( 'wpforms-hidden' ); $preview.find( '.quantity-input' ).toggleClass( 'wpforms-hidden' ); $preview.toggleClass( 'payment-quantity-enabled' ); } ); // Real-time updates for Quantity preview minimum value. $builder.on( 'input', '.wpforms-field-option-row-quantity input', function() { const $this = $( this ); // Allow only a positive integer value less than 9999. $this.val( Math.min( Math.abs( Math.round( $this.val() ) ), 9999 ) ); const $optionRow = $this.closest( '.wpforms-field-option-row' ), id = $optionRow.data( 'field-id' ), isMinInput = $this.hasClass( 'min-quantity-input' ), $minInput = $optionRow.find( '.min-quantity-input' ), $maxInput = $optionRow.find( '.max-quantity-input' ); if ( isMinInput ) { $( '#wpforms-field-' + id ).find( '.quantity-input option' ).text( $this.val() ); } $minInput.toggleClass( 'wpforms-error', parseInt( $minInput.val(), 10 ) > parseInt( $maxInput.val(), 10 ) ); } ); // Real-time updates for Date/Time, Name and Single Item "Format" option. $builder.on( 'change', '.wpforms-field-option-row-format select', function() { const $this = $( this ), value = $this.val(), id = $this.parent().data( 'field-id' ), $sublabelToggle = $( '#wpforms-field-option-row-' + id + '-sublabel_hide' ), $preview = $( '#wpforms-field-' + id ); $preview.find( '.format-selected' ).removeClass().addClass( 'format-selected format-selected-' + value ); $( '#wpforms-field-option-' + id ).find( '.format-selected' ).removeClass().addClass( 'format-selected format-selected-' + value ); // Show toggle for "Hide Sub labels" only when the field consists of more than one subfield. if ( [ 'date-time', 'first-last', 'first-middle-last' ].includes( value ) ) { $sublabelToggle.removeClass( 'wpforms-hidden' ); } else { $sublabelToggle.addClass( 'wpforms-hidden' ); } // Hide the label field if it's not a single item. $( `#wpforms-field-option-row-${ id }-price_label` ).toggleClass( 'wpforms-hidden', value !== 'single' ); // Toggle options based on Single Item "Format". if ( [ 'single', 'user', 'hidden' ].includes( value ) ) { const isUserDefined = value === 'user', isSingle = value === 'single', isHidden = value === 'hidden', isQuantityEnabled = $( '#wpforms-field-option-' + id + '-enable_quantity' ).is( ':checked' ), $minPriceOption = $( '#wpforms-field-option-' + id + '-min_price' ), minPrice = wpf.amountSanitize( $minPriceOption.val() ), isValidMinPrice = minPrice >= $minPriceOption.data( 'minimum-price' ), $minPriceOptionRow = $( '#wpforms-field-option-row-' + id + '-min_price' ); // Toggle Placeholder option. $( '#wpforms-field-option-row-' + id + '-placeholder' ).toggleClass( 'wpforms-hidden', ! isUserDefined ); // Toggle Quantity options. $( '#wpforms-field-option-row-' + id + '-enable_quantity' ).toggleClass( 'wpforms-hidden', ! isSingle ); $( '#wpforms-field-option-row-' + id + '-quantities_alert' ).toggleClass( 'wpforms-hidden', ! isSingle ); $( '#wpforms-field-option-row-' + id + '-quantity' ).toggleClass( 'wpforms-hidden', ! isSingle || ! isQuantityEnabled ); $preview.find( '.quantity-input' ).toggleClass( 'wpforms-hidden', ! isSingle || ! isQuantityEnabled ); // Toggle Minimum Price options. $minPriceOptionRow.toggleClass( 'wpforms-hidden', ! isUserDefined ); $minPriceOptionRow.find( '.wpforms-item-minimum-price-alert' ).toggleClass( 'wpforms-hidden', isValidMinPrice ); $preview.find( '.item-min-price' ).toggleClass( 'wpforms-hidden', isUserDefined && minPrice <= 0 ); $preview.toggleClass( 'min-price-warning', ! isValidMinPrice ); $preview.find( '.fa-exclamation-triangle' ).toggleClass( 'wpforms-hidden', isValidMinPrice ); // Toggle the label $( `#wpforms-field-${ id } .item-price-single` ).toggleClass( 'wpforms-hidden', ! isSingle ); $( `#wpforms-field-${ id } .item-price-hidden` ).toggleClass( 'wpforms-hidden', ! isHidden ); } } ); // Real-time updates specific for Address "Scheme" option $builder.on( 'change', '.wpforms-field-option-row-scheme select', function( e ) { // eslint-disable-line no-unused-vars const $this = $( this ); const value = $this.val(); const fieldId = $this.parent().data( 'field-id' ); const $fieldPreview = $( `#wpforms-field-${ fieldId }` ); const $stateOption = $( `#wpforms-field-option-row-${ fieldId }-state` ); const $countryOption = $( `#wpforms-field-option-row-${ fieldId }-country` ); // Switch the scheme in a Preview panel. $fieldPreview.find( '.wpforms-address-scheme' ).addClass( 'wpforms-hide' ); $fieldPreview.find( `.wpforms-address-scheme-${ value }` ).removeClass( 'wpforms-hide' ); // Show an or hide country option depending on the scheme. const $countryPreviewField = $fieldPreview.find( `.wpforms-address-scheme-${ value } .wpforms-country select, .wpforms-address-scheme-${ value } .wpforms-country input` ); $countryPreviewField.length === 0 ? $countryOption.addClass( 'wpforms-hidden' ) : $countryOption.removeClass( 'wpforms-hidden' ); // Inputs/selects for a currently selected scheme and the one that we're changing to. const $currentState = $stateOption.find( '.default .default' ).not( '.wpforms-hidden-strict' ); const $newState = $stateOption.find( `.default [data-scheme="${ value }"]` ); const $currentCountry = $countryOption.find( '.default .default' ).not( '.wpforms-hidden-strict' ); const $newCountry = $countryOption.find( `.default [data-scheme="${ value }"]` ); // Switch the state field type in options to match the scheme. $newState.attr( { id: $currentState.attr( 'id' ), name: $currentState.attr( 'name' ), } ).removeClass( 'wpforms-hidden-strict' ); $currentState.attr( { id: '', name: '' } ).addClass( 'wpforms-hidden-strict' ); $newCountry.attr( { id: $currentCountry.attr( 'id' ), name: $currentCountry.attr( 'name' ), } ).removeClass( 'wpforms-hidden-strict' ); $currentCountry.attr( { id: '', name: '' } ).addClass( 'wpforms-hidden-strict' ); } ); // Real-time updates for a Date/Time date type $builder.on( 'change', '.wpforms-field-option-row-date .type select', function( e ) { // eslint-disable-line no-unused-vars const $this = $( this ), value = $this.val(), id = $( this ).closest( '.wpforms-field-option-row' ).data( 'field-id' ), addClass = value === 'datepicker' ? 'wpforms-date-type-datepicker' : 'wpforms-date-type-dropdown', removeClass = value === 'datepicker' ? 'wpforms-date-type-dropdown' : 'wpforms-date-type-datepicker'; $( '#wpforms-field-' + id ).find( '.wpforms-date' ).addClass( addClass ).removeClass( removeClass ); $( '#wpforms-field-option-' + id ).addClass( addClass ).removeClass( removeClass ); const $limitDays = $this.closest( '.wpforms-field-option-group-advanced' ) .find( '.wpforms-field-option-row-date_limit_days, .wpforms-field-option-row-date_limit_days_options, .wpforms-field-option-row-date_disable_past_dates' ), $limitDaysOptions = $( '#wpforms-field-option-row-' + id + '-date_limit_days_options' ); if ( value === 'dropdown' ) { const $dateSelect = $( '#wpforms-field-option-' + id + '-date_format' ); if ( $dateSelect.find( 'option:selected' ).hasClass( 'datepicker-only' ) ) { $dateSelect.prop( 'selectedIndex', 0 ).trigger( 'change' ); } $limitDays.hide(); } else { $limitDays.show(); $( '#wpforms-field-option-' + id + '-date_limit_days' ).is( ':checked' ) ? $limitDaysOptions.show() : $limitDaysOptions.hide(); } } ); // Real-time updates for Date/Time date select format $builder.on( 'change', '.wpforms-field-option-row-date .format select', function( e ) { // eslint-disable-line no-unused-vars const $this = $( this ), value = $this.val(), id = $( this ).closest( '.wpforms-field-option-row' ).data( 'field-id' ), $field = $( '#wpforms-field-' + id ); if ( value === 'm/d/Y' ) { $field.find( '.wpforms-date-dropdown .first option' ).text( wpforms_builder.date_select_month ); $field.find( '.wpforms-date-dropdown .second option' ).text( wpforms_builder.date_select_day ); } else if ( value === 'd/m/Y' ) { $field.find( '.wpforms-date-dropdown .first option' ).text( wpforms_builder.date_select_day ); $field.find( '.wpforms-date-dropdown .second option' ).text( wpforms_builder.date_select_month ); } } ); // Real-time updates for Date/Time select format $builder.on( 'change', '.wpforms-field-option-row-time .format select', function( e ) { // eslint-disable-line no-unused-vars const $this = $( this ), id = $this.closest( '.wpforms-field-option-row' ).data( 'field-id' ); let options = '', hh; // Determine a time format type. // If the format contains `g` or `h`, then this is 12-hour format, otherwise 24 hours. const format = $this.val().match( /[gh]/ ) ? 12 : 24, minHour = format === 12 ? 1 : 0, maxHour = format === 12 ? 13 : 24; // Generate a new set of hour options. for ( let i = minHour; i < maxHour; i++ ) { hh = i < 10 ? '0' + i : i; options += ''.replace( /{hh}/g, hh ); } _.forEach( [ 'start', 'end' ], function( field ) { const $hour = $builder.find( '#wpforms-field-option-' + id + '-time_limit_hours_' + field + '_hour' ), $ampm = $builder.find( '#wpforms-field-option-' + id + '-time_limit_hours_' + field + '_ampm' ); let hourValue = parseInt( $hour.val(), 10 ), ampmValue = $ampm.val(); if ( format === 24 ) { hourValue = ampmValue === 'pm' ? hourValue + 12 : hourValue; } else { ampmValue = hourValue > 12 ? 'pm' : 'am'; hourValue = hourValue > 12 ? hourValue - 12 : hourValue; } hourValue = hourValue < 10 ? '0' + hourValue : hourValue; $hour.html( options ).val( hourValue ); $ampm.toggleClass( 'wpforms-hidden-strict', format === 24 ).val( ampmValue ); $ampm.nextAll( 'div' ).toggleClass( 'wpforms-hidden-strict', format === 12 ); } ); } ); // Consider the field active when a disabled nav button is clicked $builder.on( 'click', '.wpforms-pagebreak-button', function( e ) { e.preventDefault(); $( this ).closest( '.wpforms-field' ).trigger( 'click' ); } ); /* * Pagebreak field. */ app.fieldPageBreakInitDisplayPrevious( $builder.find( '.wpforms-field-pagebreak.wpforms-pagebreak-normal' ).first() ); $builder .on( 'input', '.wpforms-field-option-row-next input', function( e ) { // eslint-disable-line no-unused-vars // Real-time updates for "Next" pagebreak field option. const $this = $( this ), value = $this.val(), $next = $( '#wpforms-field-' + $this.parent().data( 'field-id' ) ).find( '.wpforms-pagebreak-next' ); if ( value ) { $next.css( 'display', 'inline-block' ).text( value ); } else { $next.css( 'display', 'none' ).empty(); } } ) .on( 'input', '.wpforms-field-option-row-prev input', function( e ) { // eslint-disable-line no-unused-vars // Real-time updates for "Prev" pagebreak field option. const $this = $( this ), value = $this.val().trim(), $field = $( '#wpforms-field-' + $this.parent().data( 'field-id' ) ), $prevBtn = $field.find( '.wpforms-pagebreak-prev' ); if ( value && $field.prevAll( '.wpforms-field-pagebreak.wpforms-pagebreak-normal' ).length > 0 ) { $prevBtn.removeClass( 'wpforms-hidden' ).text( value ); } else { $prevBtn.addClass( 'wpforms-hidden' ).empty(); } } ) .on( 'change', '.wpforms-field-option-row-prev_toggle input', function( e ) { // eslint-disable-line complexity, no-unused-vars // Real-time updates for "Display Previous" pagebreak field option. const $input = $( this ), $wrapper = $input.closest( '.wpforms-field-option-row-prev_toggle' ); if ( $wrapper.hasClass( 'wpforms-entry-preview-block' ) ) { return; } const $prev = $input.closest( '.wpforms-field-option-group-inner' ).find( '.wpforms-field-option-row-prev' ); const $prevLabel = $prev.find( 'input' ), $prevBtn = $( '#wpforms-field-' + $input.closest( '.wpforms-field-option' ).data( 'field-id' ) ).find( '.wpforms-pagebreak-prev' ); $prev.toggleClass( 'wpforms-hidden', ! $input.prop( 'checked' ) ); $prevBtn.toggleClass( 'wpforms-hidden', ! $input.prop( 'checked' ) ); if ( $input.prop( 'checked' ) && ! $prevLabel.val() ) { let message = $prevLabel.data( 'last-value' ); message = message && message.trim() ? message.trim() : wpforms_builder.previous; $prevLabel.val( message ); } // Backward compatibility for forms that were created before the toggle was added. if ( ! $input.prop( 'checked' ) ) { $prevLabel.data( 'last-value', $prevLabel.val() ); $prevLabel.val( '' ); } $prevLabel.trigger( 'input' ); } ) .on( 'wpformsFieldAdd', app.fieldPagebreakAdd ) .on( 'wpformsFieldDelete', app.fieldPagebreakDelete ) .on( 'wpformsFieldAdd', app.toggleOrderSummaryConfirmation ) .on( 'wpformsFieldDelete', app.toggleOrderSummaryConfirmation ) .on( 'wpformsBeforeFieldDelete', app.fieldEntryPreviewDelete ); // Update Display Previous option visibility for all Pagebreak fields. $builder.on( 'wpformsFieldMove wpformsFieldAdd wpformsFieldDelete', function( e ) { // eslint-disable-line no-unused-vars $builder.find( '.wpforms-field-pagebreak.wpforms-pagebreak-normal' ).each( function( i ) { // eslint-disable-line no-unused-vars app.fieldPageBreakInitDisplayPrevious( $( this ) ); } ); } ); // Real-time updates for "Page Title" pagebreak field option $builder.on( 'input', '.wpforms-field-option-row-title input', function( e ) { // eslint-disable-line no-unused-vars const $this = $( this ), value = $this.val(), id = $this.parent().data( 'field-id' ); if ( value ) { $( '#wpforms-field-' + id ).find( '.wpforms-pagebreak-title' ).text( value ); } else { $( '#wpforms-field-' + id ).find( '.wpforms-pagebreak-title' ).empty(); } } ); // Real-time updates for "Page Navigation Alignment" pagebreak field option $builder.on( 'change', '.wpforms-field-option-row-nav_align select', function( e ) { // eslint-disable-line no-unused-vars const $this = $( this ); let value = $this.val(); if ( ! value ) { value = 'center'; } $( '.wpforms-pagebreak-buttons' ) .removeClass( 'wpforms-pagebreak-buttons-center wpforms-pagebreak-buttons-left wpforms-pagebreak-buttons-right wpforms-pagebreak-buttons-split' ) .addClass( 'wpforms-pagebreak-buttons-' + value ); } ); // Real-time updates for Single Item field "Item Price" option. $builder.on( 'input', '.wpforms-field-option-row-price input', function() { const $this = $( this ), value = $this.val(), formatted = wpf.amountFormat( wpf.amountSanitize( value ) ), id = $this.parent().data( 'field-id' ), placeholder = $( '#wpforms-field-option-' + id + '-placeholder' ).val().trim(), $preview = $( '#wpforms-field-' + id ), newValue = value === '' && placeholder !== '' ? '' : formatted; $preview.find( '.primary-input' ).val( newValue ); $preview.find( '.price' ).text( wpf.amountFormatCurrency( value ) ); } ); // Real-time updates for Single Item field "Minimum Price" option. $builder.on( 'input', '.wpforms-field-option-row-min_price input', function() { const $this = $( this ), amount = $this.val(), sanitized = wpf.amountSanitize( amount ), isEmpty = sanitized <= 0, isValid = sanitized >= $this.data( 'minimum-price' ), $fieldOptionRow = $this.parent(), $preview = $( '#wpforms-field-' + $fieldOptionRow.data( 'field-id' ) ); $fieldOptionRow.find( '.wpforms-item-minimum-price-alert' ).toggleClass( 'wpforms-hidden', isValid ); $preview.find( '.item-min-price' ).toggleClass( 'wpforms-hidden', isEmpty ); $preview.toggleClass( 'min-price-warning', ! isValid ); $preview.find( '.fa-exclamation-triangle' ).toggleClass( 'wpforms-hidden', isValid ); if ( isEmpty ) { return; } $preview.find( '.min-price' ).text( wpf.amountFormatCurrency( amount ) ); } ); // Real-time updates for price label for single item field. $builder.on( 'input', '.wpforms-single-item-price-label-display', function() { const $this = $( this ), value = wpf.sanitizeHTML( $this.val(), '<>' ), id = $this.parent().data( 'field-id' ), $preview = $( `#wpforms-field-${ id }` ), $price = wpf.amountFormatCurrency( $( `#wpforms-field-option-${ id }-price` ).val() ); if ( ! value ) { $this.val( '{price}' ); $preview.find( '.price-label' ).html( ` ${ $price } ` ); return; } $preview.find( '.price-label' ).html( value.replaceAll( '{price}', ` ${ $price } ` ) ); } ); // Real-time updates for payment CC icons $builder.on( 'change', '.wpforms-field-option-credit-card .payment-icons input', function() { const $this = $( this ), card = $this.data( 'card' ), id = $this.parent().data( 'field-id' ); $( '#wpforms-field-' + id ).find( 'img.icon-' + card ).toggleClass( 'card_hide' ); } ); // Generic updates for various additional placeholder fields (at least Stripe's "Name on Card"). $builder.on( 'input', '.wpforms-field-option input.placeholder-update', function( e ) { // eslint-disable-line no-unused-vars const $this = $( this ), value = $this.val(), id = $this.data( 'field-id' ), subfield = $this.data( 'subfield' ); $( '#wpforms-field-' + id ).find( '.wpforms-' + subfield + ' input' ).attr( 'placeholder', value ); } ); // Toggle Choice Layout advanced field option. $builder.on( 'change', '.wpforms-field-option-row-input_columns select', function() { const $this = $( this ), value = $this.val(), id = $this.parent().data( 'field-id' ); let cls = ''; if ( value === '2' ) { cls = 'wpforms-list-2-columns'; } else if ( value === '3' ) { cls = 'wpforms-list-3-columns'; } else if ( value === 'inline' ) { cls = 'wpforms-list-inline'; } $( '#wpforms-field-' + id ).removeClass( 'wpforms-list-2-columns wpforms-list-3-columns wpforms-list-inline' ).addClass( cls ); } ); // Toggle the toggle field. $builder.on( 'change', '.wpforms-field-option-row .wpforms-toggle-control input', function( e ) { // eslint-disable-line no-unused-vars const $check = $( this ), $control = $check.closest( '.wpforms-toggle-control' ), $status = $control.find( '.wpforms-toggle-control-status' ), state = $check.is( ':checked' ) ? 'on' : 'off'; $status.html( $status.data( state ) ); } ); // Real-time updates for "Dynamic Choices" field option, for Dropdown, // Checkboxes, and Multiple choice fields $builder.on( 'change', '.wpforms-field-option-row-dynamic_choices select', function( e ) { // eslint-disable-line no-unused-vars app.fieldDynamicChoiceToggle( $( this ) ); } ); // Real-time updates for "Dynamic [type] Source" field option, for Dropdown, // Checkboxes, and Multiple choice fields $builder.on( 'change', '.wpforms-field-option-row-dynamic_taxonomy select, .wpforms-field-option-row-dynamic_post_type select', function( e ) { // eslint-disable-line no-unused-vars app.fieldDynamicChoiceSource( $( this ) ); } ); // Toggle Layout selector $builder.on( 'click', '.toggle-layout-selector-display', function( e ) { e.preventDefault(); app.fieldLayoutSelectorToggle( this ); } ); $builder.on( 'click', '.layout-selector-display-layout', function( e ) { e.preventDefault(); app.fieldLayoutSelectorLayout( this ); } ); $builder.on( 'click', '.layout-selector-display-columns span', function( e ) { e.preventDefault(); app.fieldLayoutSelectorInsert( this ); } ); // Real-time updates for a Rating field scale option. $( document ).on( 'change', '.wpforms-field-option-row-scale select', function() { const $this = $( this ), value = $this.val(), id = $this.parent().data( 'field-id' ), $icons = $( '#wpforms-field-' + id + ' .rating-icon' ); let x = 1; $icons.each( function( index ) { // eslint-disable-line no-unused-vars if ( x <= value ) { $( this ).show(); } else { $( this ).hide(); } x++; } ); } ); // Real-time updates for a Rating field icon option. $( document ).on( 'change', '.wpforms-field-option-row-icon select', function() { const $this = $( this ), value = $this.val(), id = $this.parent().data( 'field-id' ), $icons = $( '#wpforms-field-' + id + ' .rating-icon' ); let iconClass = 'fa-star'; if ( 'heart' === value ) { iconClass = 'fa-heart'; } else if ( 'thumb' === value ) { iconClass = 'fa-thumbs-up'; } else if ( 'smiley' === value ) { iconClass = 'fa-smile-o'; } $icons.removeClass( 'fa-star fa-heart fa-thumbs-up fa-smile-o' ).addClass( iconClass ); } ); // Real-time updates for a Rating field icon size option. $( document ).on( 'change', '.wpforms-field-option-row-icon_size select', function() { const $this = $( this ), value = $this.val(), id = $this.parent().data( 'field-id' ), $icons = $( '#wpforms-field-' + id + ' .rating-icon' ); let fontSize = '28'; if ( 'small' === value ) { fontSize = '18'; } else if ( 'large' === value ) { fontSize = '38'; } $icons.css( 'font-size', fontSize + 'px' ); } ); // Real-time updates for a Rating field icon color option. $( document ).on( 'input', '.wpforms-field-option-row-icon_color input.wpforms-color-picker', function() { const $this = $( this ), id = $this.closest( '.wpforms-field-option-row' ).data( 'field-id' ), $icons = $( '#wpforms-field-' + id + ' > i.fa' ); $icons.css( 'color', app.getValidColorPickerValue( $this ) ); } ); // Real-time updates for a Checkbox field Disclaimer option. $( document ).on( 'change', '.wpforms-field-option-row-disclaimer_format input', function() { const $this = $( this ), id = $this.closest( '.wpforms-field-option-row' ).data( 'field-id' ), $desc = $( '#wpforms-field-' + id + ' .description' ); $desc.toggleClass( 'disclaimer' ); } ); $builder.on( 'change', '.wpforms-field-option-row-limit_enabled input', function( event ) { app.updateTextFieldsLimitControls( $( event.target ).closest( '.wpforms-field-option-row-limit_enabled' ).data().fieldId, event.target.checked ); } ); $builder.on( 'change', '.wpforms-field-option-row-date_disable_past_dates input', function( event ) { app.updateDisableTodaysDateControls( $( event.target ).closest( '.wpforms-field-option-row-date_disable_past_dates' ).data().fieldId, event.target?.checked ); } ); $builder.on( 'change', '.wpforms-field-option-row-password-strength input', function( event ) { app.updatePasswordStrengthControls( $( event.target ).parents( '.wpforms-field-option-row-password-strength' ).data().fieldId, event.target.checked ); } ); $builder.on( 'change', '.wpforms-field-option-richtext .wpforms-field-option-row-media_enabled input', app.updateRichTextMediaFieldsLimitControls ); $builder.on( 'change', '.wpforms-field-option-richtext .wpforms-field-option-row-style select', app.updateRichTextStylePreview ); // File uploader - change style. $builder .on( 'change', '.wpforms-field-option-file-upload .wpforms-field-option-row-style select, .wpforms-field-option-file-upload .wpforms-field-option-row-max_file_number input', function( event ) { app.fieldFileUploadPreviewUpdate( event.target ); } ); // Real-time updates for Number Slider field. app.numberSliderEvents( $builder ); // Hide image and icon choices if dynamic choices are not off. app.fieldDynamicChoiceToggleImageChoices(); app.fieldDynamicChoiceToggleIconChoices(); // Real-time updates for Payment field's 'Show price after item label' option. $builder.on( 'change', '.wpforms-field-option-row-show_price_after_labels input', function( e ) { // eslint-disable-line no-unused-vars const $input = $( this ), $list = $input.closest( '.wpforms-field-option-group-basic' ).find( '.wpforms-field-option-row-choices .choices-list' ); app.fieldChoiceUpdate( $list.data( 'field-type' ), $list.data( 'field-id' ) ); } ); $builder .on( 'input', '.wpforms-field-option-row-preview-notice textarea', app.updatePreviewNotice ) .on( 'change', '.wpforms-field-option-row-preview-notice-enable input', app.toggleEntryPreviewNotice ) .on( 'wpformsFieldAdd', app.maybeLockEntryPreviewGroupOnAdd ) .on( 'wpformsFieldMove', app.maybeLockEntryPreviewGroupOnMove ) .on( 'click', '.wpforms-entry-preview-block', app.entryPreviewBlockField ); app.defaultStateEntryPreviewNotice(); }, /** * Check if we had focusout event from certain fields. * * @since 1.7.5 */ focusOutEvent() { if ( elements.$focusOutTarget === null ) { return; } if ( elements.$defaultEmail.is( elements.$focusOutTarget ) ) { const $field = elements.$focusOutTarget; $field.next( '.wpforms-alert' ).remove(); if ( $field.val() === '' ) { return; } $.get( wpforms_builder.ajax_url, { nonce: wpforms_builder.nonce, content: $field.val(), action: 'wpforms_sanitize_default_email', }, function( res ) { if ( res.success ) { $field.val( res.data ); $field.trigger( 'input' ); if ( ! res.data ) { $field.after( '

    ' + wpforms_builder.restricted_default_email + '

    ' ); } } } ); } elements.$focusOutTarget = null; }, /** * Determine if the field is disabled for selection/duplication/deletion. * * @since 1.7.1 * * @param {any} el DOM element or jQuery object of some container on the field preview. * * @return {boolean} True if actions are disabled. */ isFieldPreviewActionsDisabled( el ) { return app.isFormPreviewActionsDisabled( el ) || $( el ).closest( '.wpforms-field' ).hasClass( 'ui-sortable-disabled' ); }, /** * Determine if form wrapper has sorting locked. * * @since 1.7.6 * * @param {any} el DOM element or jQuery object of some container on the field preview. * * @return {boolean} True when form preview wrapper sorting is disabled. */ isFormPreviewActionsDisabled( el ) { return $( el ).closest( '.wpforms-field-wrap' ).hasClass( 'ui-sortable-disabled' ); }, /** * Toggle field group visibility in the field sidebar. * * @since 1.0.0 * * @param {any} el DOM element or jQuery object. * @param {string} action Action. */ fieldGroupToggle( el, action ) { const $this = $( el ); let $buttons = $this.next( '.wpforms-add-fields-buttons' ); const $group = $buttons.parent(); let $icon = $this.find( 'i' ), groupName = $this.data( 'group' ), cookieName = 'wpforms_field_group_' + groupName; if ( action === 'click' ) { if ( $group.hasClass( 'wpforms-closed' ) ) { wpCookies.remove( cookieName ); } else { wpCookies.set( cookieName, 'true', 2592000 ); // 1 month } $icon.toggleClass( 'wpforms-angle-right' ); $buttons.stop().slideToggle( '', function() { $group.toggleClass( 'wpforms-closed' ); } ); return; } if ( action === 'load' ) { $buttons = $this.find( '.wpforms-add-fields-buttons' ); $icon = $this.find( '.wpforms-add-fields-heading i' ); groupName = $this.find( '.wpforms-add-fields-heading' ).data( 'group' ); cookieName = 'wpforms_field_group_' + groupName; if ( wpCookies.get( cookieName ) === 'true' ) { $icon.toggleClass( 'wpforms-angle-right' ); $buttons.hide(); $this.toggleClass( 'wpforms-closed' ); } } }, /** * Update description. * * @since 1.6.9 * * @param {jQuery} $el Element. * @param {string} value Value. */ updateDescription( $el, value ) { if ( $el.hasClass( 'nl2br' ) ) { value = value.replace( /\n/g, '
    ' ); } $el.html( value ); }, /** * Set the default state for the entry preview notice field. * * @since 1.6.9 */ defaultStateEntryPreviewNotice() { $( '.wpforms-field-option-row-preview-notice-enable input' ).each( function() { $( this ).trigger( 'change' ); } ); }, /** * Update a preview notice for the field preview. * * @since 1.6.9 */ updatePreviewNotice() { const $this = $( this ); let value = wpf.sanitizeHTML( $this.val() ).trim(); const id = $this.parent().data( 'field-id' ), $field = $( '#wpforms-field-' + id ).find( '.wpforms-entry-preview-notice' ); value = value ? value : wpforms_builder.entry_preview_default_notice; app.updateDescription( $field, value ); }, /** * Show/hide entry preview notice for the field preview. * * @since 1.6.9 */ toggleEntryPreviewNotice() { const $this = $( this ), id = $this.closest( '.wpforms-field-option' ).data( 'field-id' ), $field = $( '#wpforms-field-' + id ), $noticeField = $( '#wpforms-field-option-' + id + ' .wpforms-field-option-row-preview-notice' ), $notice = $field.find( '.wpforms-entry-preview-notice' ), $defaultNotice = $field.find( '.wpforms-alert-info' ); if ( $this.is( ':checked' ) ) { $defaultNotice.hide(); $notice.show(); $noticeField.show(); return; } $noticeField.hide(); $notice.hide(); $defaultNotice.show(); }, /** * Delete a field. * * @param {number} id Field ID. * * @since 1.0.0 * @since 1.6.9 Add the entry preview logic. */ fieldDelete( id ) { const $field = $( '#wpforms-field-' + id ), type = $field.data( 'field-type' ); if ( type === 'pagebreak' && $field.hasClass( 'wpforms-field-entry-preview-not-deleted' ) ) { app.youCantRemovePageBreakFieldPopup(); return; } if ( $field.hasClass( 'no-delete' ) ) { app.youCantRemoveFieldPopup(); return; } app.confirmFieldDeletion( id, type ); }, /** * Show the error message in the popup that you cannot remove the page break field. * * @since 1.6.9 */ youCantRemovePageBreakFieldPopup() { $.alert( { title: wpforms_builder.heads_up, content: wpforms_builder.entry_preview_require_page_break, icon: 'fa fa-exclamation-circle', type: 'red', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); }, /** * Show the error message in the popup that you cannot reorder the field. * * @since 1.7.1 * @since 1.7.7 Deprecated. * * @deprecated Use `WPForms.Admin.Builder.DragFields.youCantReorderFieldPopup()` instead. */ youCantReorderFieldPopup() { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "WPFormsBuilder.youCantReorderFieldPopup()" has been deprecated, please use the new "WPForms.Admin.Builder.DragFields.youCantReorderFieldPopup()" function instead!' ); WPForms.Admin.Builder.DragFields.youCantReorderFieldPopup(); }, /** * Show the error message in the popup that you cannot remove the field. * * @since 1.6.9 */ youCantRemoveFieldPopup() { $.alert( { title: wpforms_builder.field_locked, content: wpforms_builder.field_locked_no_delete_msg, icon: 'fa fa-info-circle', type: 'blue', buttons: { confirm: { text: wpforms_builder.close, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); }, /** * Error alert displayed for invalid From Email Notification field. * * @since 1.8.1 * * @param {string} $msg Message. */ validationErrorNotificationPopup( $msg ) { $.alert( { title: wpforms_builder.heads_up, content: $msg, icon: 'fa fa-exclamation-circle', type: 'red', buttons: { confirm: { text: wpforms_builder.close, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); }, /** * Show the confirmation popup before the field deletion. * * @param {number} id Field ID. * @param {string} type Field type. * * @since 1.6.9 */ confirmFieldDeletion( id, type ) { const fieldData = { id, message: wpforms_builder.delete_confirm, }; const event = WPFormsUtils.triggerEvent( $builder, 'wpformsBeforeFieldDeleteAlert', [ fieldData, type ] ); // Allow callbacks on `wpformsBeforeFieldDeleteAlert` to prevent field deletion by triggering `event.preventDefault()`. if ( event.isDefaultPrevented() ) { return; } $.confirm( { title: false, content: fieldData.message, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action() { app.fieldDeleteById( id ); }, }, cancel: { text: wpforms_builder.cancel, }, }, } ); }, /** * Remove the field by ID. * * @since 1.6.9 * * @param {number} id Field ID. * @param {string} type Field type (deprecated) * @param {number} duration Duration of animation. */ fieldDeleteById( id, type = '', duration = 400 ) { $( `#wpforms-field-${ id }` ).fadeOut( duration, function() { const $field = $( this ); const $layoutParents = $field.parents( '.wpforms-field-layout-columns' ); type = $field.data( 'field-type' ); $builder.trigger( 'wpformsBeforeFieldDelete', [ id, type ] ); $field.remove(); $( '#wpforms-field-option-' + id ).remove(); $( '.wpforms-field, .wpforms-title-desc' ).removeClass( 'active' ); app.fieldTabToggle( 'add-fields' ); const $fieldsOptions = $( '.wpforms-field-option' ), $submitButton = $builder.find( '.wpforms-field-submit' ); // No fields remains. if ( $fieldsOptions.length < 1 ) { elements.$sortableFieldsWrap.append( elements.$noFieldsPreview.clone() ); elements.$fieldOptions.append( elements.$noFieldsOptions.clone() ); $submitButton.hide(); } // Only Layout fields remains. if ( ! $fieldsOptions.filter( ':not(.wpforms-field-option-layout)' ).length ) { $submitButton.hide(); } $builder.trigger( 'wpformsFieldDelete', [ id, type, $layoutParents ] ); } ); }, /** * Determine which sections to activate for each panel. * * @since 1.9.3 */ determineActiveSections() { const sectionFromUrl = wpf.getQueryString( 'section' ); // Gets the section to activate based on the URL. const getSectionFromUrl = ( $panel, sectionFromUrl ) => { if ( ! sectionFromUrl || ! $panel.hasClass( 'active' ) ) { return null; } const $sectionElement = $panel.find( `.wpforms-panel-sidebar-section[data-section="${ sectionFromUrl }"]` ); return $sectionElement.length ? $sectionElement : null; }; // Gets the configured section within a panel to activate, if available. const getConfiguredSection = ( $panel ) => { const $configuredSection = $panel.find( '.wpforms-panel-sidebar-section.configured' ).first(); return $configuredSection.length ? $configuredSection : null; }; // Gets the first available section in the sidebar to activate. const getFirstAvailableSection = ( $panel ) => { return $panel.find( '.wpforms-panel-sidebar-section:first-of-type' ); }; // Activates the specified section within a panel and its corresponding content section. const activateSection = ( $panel, $sectionToActivate ) => { if ( ! $sectionToActivate ) { return; } const sectionNameToActivate = $sectionToActivate.data( 'section' ); $sectionToActivate.addClass( 'active' ); const $contentSection = $panel.find( `.wpforms-panel-content-section-${ sectionNameToActivate }` ); if ( $contentSection.length ) { $contentSection.show().addClass( 'active' ); $panel.find( '.wpforms-panel-content-section-default' ).toggle( sectionNameToActivate === 'default' ); } else { $panel.find( '.wpforms-panel-content-section-default' ).show().addClass( 'active' ); } WPFormsUtils.triggerEvent( $builder, 'wpformsPanelSectionSwitch', sectionNameToActivate ); }; // Iterate through each panel and determine which section to activate. $( '.wpforms-panel' ).each( function( index, el ) { // eslint-disable-line no-unused-vars const $panel = $( this ); const $sectionToActivate = getSectionFromUrl( $panel, sectionFromUrl ) || getConfiguredSection( $panel ) || getFirstAvailableSection( $panel ); activateSection( $panel, $sectionToActivate ); } ); }, /** * Load entry preview fields. * * @since 1.6.9 */ loadEntryPreviewFields() { const $fields = $( '#wpforms-panel-fields .wpforms-field-wrap .wpforms-field-entry-preview' ); if ( ! $fields.length ) { return; } $fields.each( function() { app.lockEntryPreviewFieldsPosition( $( this ).data( 'field-id' ) ); } ); }, /** * Delete the entry preview field from the form preview. * * @since 1.6.9 * * @param {Event} event Event. * @param {number} id Field ID. * @param {string} type Field type. */ fieldEntryPreviewDelete( event, id, type ) { if ( 'entry-preview' !== type ) { return; } const $field = $( '#wpforms-field-' + id ), $previousPageBreakField = $field.prevAll( '.wpforms-field-pagebreak' ).first(), $nextPageBreakField = $field.nextAll( '.wpforms-field-pagebreak' ).first(), nextPageBreakId = $nextPageBreakField.data( 'field-id' ), $nextPageBreakOptions = $( '#wpforms-field-option-' + nextPageBreakId ); $previousPageBreakField.removeClass( 'wpforms-field-not-draggable wpforms-field-entry-preview-not-deleted' ); $nextPageBreakOptions.find( '.wpforms-entry-preview-block' ).removeClass( 'wpforms-entry-preview-block' ); $builder.trigger( 'wpformsFieldDragToggle', [ $previousPageBreakField.data( 'field-id' ), $previousPageBreakField.data( 'field-type' ) ] ); }, /** * Maybe lock the entry preview and fields nearby after move event. * * @since 1.6.9 * * @param {Event} e Event. * @param {Object} ui UI sortable object. */ maybeLockEntryPreviewGroupOnMove( e, ui ) { if ( ! ui.item.hasClass( 'wpforms-field-pagebreak' ) ) { return; } app.maybeLockEntryPreviewGroupOnAdd( e, ui.item.data( 'field-id' ), 'pagebreak' ); }, /** * Maybe lock the entry preview and fields nearby after adding event. * * @since 1.6.9 * * @param {Event} e Event. * @param {number} fieldId Field id. * @param {string} type Field type. */ maybeLockEntryPreviewGroupOnAdd( e, fieldId, type ) { if ( type !== 'pagebreak' ) { return; } const $currentField = $( '#wpforms-field-' + fieldId ), $prevField = $currentField.prevAll( '.wpforms-field-entry-preview,.wpforms-field-pagebreak' ).first(), $nextField = $currentField.nextAll( '.wpforms-field-entry-preview,.wpforms-field-pagebreak' ).first(); if ( ! $prevField.hasClass( 'wpforms-field-entry-preview' ) && ! $nextField.hasClass( 'wpforms-field-entry-preview' ) ) { return; } const $currentFieldPrevToggle = $( '#wpforms-field-option-' + fieldId + ' .wpforms-field-option-row-prev_toggle' ), $currentFieldPrevToggleField = $currentFieldPrevToggle.find( 'input' ), $nextFieldPrevToggle = $( '#wpforms-field-option-' + $nextField.data( 'field-id' ) + ' .wpforms-field-option-row-prev_toggle' ); if ( $prevField.hasClass( 'wpforms-field-entry-preview' ) ) { $currentFieldPrevToggleField.attr( 'checked', 'checked' ).trigger( 'change' ); $currentFieldPrevToggle.addClass( 'wpforms-entry-preview-block' ); $nextFieldPrevToggle.removeClass( 'wpforms-entry-preview-block' ); return; } const prevFieldId = $prevField.data( 'field-id' ), $prevFieldPrevToggle = $( '#wpforms-field-option-' + prevFieldId + ' .wpforms-field-option-row-prev_toggle' ), $prevFieldPrevToggleField = $prevFieldPrevToggle.find( 'input' ); $currentField.addClass( 'wpforms-field-not-draggable wpforms-field-entry-preview-not-deleted' ); $builder.trigger( 'wpformsFieldDragToggle', [ fieldId, type ] ); $prevField.removeClass( 'wpforms-field-not-draggable wpforms-field-entry-preview-not-deleted' ); $builder.trigger( 'wpformsFieldDragToggle', [ prevFieldId, $prevField.data( 'field-type' ) ] ); if ( $prevField.prevAll( '.wpforms-field-entry-preview,.wpforms-field-pagebreak' ).first().hasClass( 'wpforms-field-entry-preview' ) ) { $prevFieldPrevToggleField.attr( 'checked', 'checked' ).trigger( 'change' ); $prevFieldPrevToggle.addClass( 'wpforms-entry-preview-block' ); } }, /** * Show the error popup that the entry preview field blocks the field. * * @since 1.6.9 * * @param {Event} e Event. */ entryPreviewBlockField( e ) { e.preventDefault(); $.alert( { title: wpforms_builder.heads_up, content: wpforms_builder.entry_preview_require_previous_button, icon: 'fa fa-exclamation-circle', type: 'red', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); }, /** * Is it an entry preview field that should be checked before adding? * * @since 1.6.9 * * @param {string} type Field type. * @param {Object} options Field options. * * @return {boolean} True when we should check it. */ isUncheckedEntryPreviewField( type, options ) { // eslint-disable-next-line no-mixed-operators return type === 'entry-preview' && ( ! options || options && ! options.passed ); }, /** * Add an entry preview field to the form preview. * * @since 1.6.9 * * @param {string} type Field type. * @param {Object} options Field options. */ addEntryPreviewField( type, options ) { // eslint-disable-line complexity const addButton = $( '#wpforms-add-fields-entry-preview' ); if ( addButton.hasClass( 'wpforms-entry-preview-adding' ) ) { return; } const $fields = $( '#wpforms-panel-fields .wpforms-field-wrap > .wpforms-field' ), position = options?.position ? options.position : $fields.length, needPageBreakBefore = app.isEntryPreviewFieldRequiresPageBreakBefore( $fields, position ), needPageBreakAfter = app.isEntryPreviewFieldRequiresPageBreakAfter( $fields, position ); addButton.addClass( 'wpforms-entry-preview-adding' ); if ( ! options ) { options = {}; } options.passed = true; if ( ! needPageBreakBefore && ! needPageBreakAfter ) { app.fieldAdd( 'entry-preview', options ).done( function( res ) { app.lockEntryPreviewFieldsPosition( res.data.field.id ); } ); return; } if ( needPageBreakBefore ) { app.addPageBreakAndEntryPreviewFields( options, position ); return; } app.addEntryPreviewAndPageBreakFields( options, position ); }, /** * Add the entry preview field after the page break field. * We should wait for the page break adding to avoid id duplication. * * @since 1.6.9 * * @param {Object} options Field options. */ addEntryPreviewFieldAfterPageBreak( options ) { const checkExist = setInterval( function() { if ( $( '#wpforms-panel-fields .wpforms-field-wrap' ).find( '.wpforms-pagebreak-bottom, .wpforms-pagebreak-top' ).length === 2 ) { app.fieldAdd( 'entry-preview', options ).done( function( res ) { app.lockEntryPreviewFieldsPosition( res.data.field.id ); } ); clearInterval( checkExist ); } }, 100 ); }, /** * Add the entry preview field after the page break field. * * @since 1.6.9 * * @param {Object} options Field options. * @param {number} position The field position. */ addPageBreakAndEntryPreviewFields( options, position ) { const hasPageBreak = $( '#wpforms-panel-fields .wpforms-field-wrap > .wpforms-field-pagebreak' ).length >= 3; app.fieldAdd( 'pagebreak', { position } ).done( function( res ) { options.position = hasPageBreak ? position + 1 : position + 2; app.addEntryPreviewFieldAfterPageBreak( options ); const $pageBreakOptions = $( '#wpforms-field-option-' + res.data.field.id ), $pageBreakPrevToggle = $pageBreakOptions.find( '.wpforms-field-option-row-prev_toggle' ), $pageBreakPrevToggleField = $pageBreakPrevToggle.find( 'input' ); $pageBreakPrevToggleField.attr( 'checked', 'checked' ).trigger( 'change' ); $pageBreakPrevToggle.addClass( 'wpforms-entry-preview-block' ); } ); }, /** * Duplicate field. * * @since 1.2.9 * * @param {string} id Field id. */ fieldDuplicate( id ) { const $field = $( `#wpforms-field-${ id }` ); if ( $field.hasClass( 'no-duplicate' ) ) { $.alert( { title: wpforms_builder.field_locked, content: wpforms_builder.field_locked_no_duplicate_msg, icon: 'fa fa-info-circle', type: 'blue', buttons: { confirm: { text: wpforms_builder.close, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); return; } $.confirm( { title: false, content: wpforms_builder.duplicate_confirm, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action() { // Disable the current button to avoid firing multiple click events. // By default, "jconfirm" tends to destroy any modal DOM element upon button click. this.$$confirm.prop( 'disabled', true ); const beforeEvent = WPFormsUtils.triggerEvent( $builder, 'wpformsBeforeFieldDuplicate', [ id, $field ] ); // Allow callbacks on `wpformsFieldBeforeDuplicate` to cancel field duplication. if ( beforeEvent.isDefaultPrevented() ) { return; } const newFieldId = app.fieldDuplicateRoutine( id, true ), $newField = $( `#wpforms-field-${ newFieldId }` ); // Lastly, update the next ID stored in the database. app.increaseNextFieldIdAjaxRequest(); WPFormsUtils.triggerEvent( $builder, 'wpformsFieldDuplicated', [ id, $field, newFieldId, $newField ] ); }, }, cancel: { text: wpforms_builder.cancel, }, }, } ); }, /** * Update the next ID stored in the database. * * @since 1.7.7 */ increaseNextFieldIdAjaxRequest() { /* eslint-disable camelcase */ $.post( wpforms_builder.ajax_url, { form_id: s.formID, field_id: elements.$nextFieldId.val(), nonce: wpforms_builder.nonce, action: 'wpforms_builder_increase_next_field_id', } ); }, /** * Duplicate field routine. * * @since 1.7.7 * * @param {number|string} id Field ID. * @param {boolean} changeLabel Is it necessary to change the label and add a copy suffix. * * @return {number} New field ID. */ fieldDuplicateRoutine( id, changeLabel = true ) { // eslint-disable-line max-lines-per-function, complexity const $field = $( `#wpforms-field-${ id }` ), $fieldOptions = $( `#wpforms-field-option-${ id }` ), $fieldActive = elements.$sortableFieldsWrap.find( '>.active' ), $visibleOptions = elements.$fieldOptions.find( '>:visible' ), $visibleTab = $visibleOptions.find( '>.active' ), type = $field.data( 'field-type' ), fieldOptionsClass = $fieldOptions.attr( 'class' ), isModernDropdown = app.dropdownField.helpers.isModernSelect( $field.find( '> .choices .primary-input' ) ); // Restore tooltips before cloning. wpf.restoreTooltips( $fieldOptions ); // Force Modern Dropdown conversion to classic before cloning. if ( isModernDropdown ) { app.dropdownField.helpers.convertModernToClassic( id ); } let newFieldOptions = $fieldOptions.html(); const $newField = $field.clone(), newFieldID = parseInt( elements.$nextFieldId.val(), 10 ), $fieldLabel = $( `#wpforms-field-option-${ id }-label` ), fieldLabelVal = $fieldLabel.length ? $fieldLabel.val() : $( `#wpforms-field-option-${ id }-name` ).val(), nextID = newFieldID + 1, regex = {}; const newFieldLabel = fieldLabelVal !== '' ? `${ fieldLabelVal } ${ wpforms_builder.duplicate_copy }` : `${ wpforms_builder.field } #${ id } ${ wpforms_builder.duplicate_copy }`; regex.fieldOptionsID = new RegExp( 'ID #' + id, 'g' ); regex.fieldID = new RegExp( 'fields\\[' + id + '\\]', 'g' ); regex.dataFieldID = new RegExp( 'data-field-id="' + id + '"', 'g' ); regex.referenceID = new RegExp( 'data-reference="' + id + '"', 'g' ); regex.elementID = new RegExp( '\\b(id|for)="wpforms-(.*?)' + id + '(.*?)"', 'ig' ); // Toggle visibility states. $field.after( $newField ); $fieldActive.removeClass( 'active' ); $newField.addClass( 'active' ).attr( { id: `wpforms-field-${ newFieldID }`, 'data-field-id': newFieldID, } ); // Various regexes to adjust the field options to work with the new field ID. regex.elementIdReplace = function( match, p1, p2, p3, offset, string ) { // eslint-disable-line no-unused-vars return `${ p1 }="wpforms-${ p2 }${ newFieldID }${ p3 }"`; }; newFieldOptions = newFieldOptions.replace( regex.fieldOptionsID, `ID #${ newFieldID }` ); newFieldOptions = newFieldOptions.replace( regex.fieldID, `fields[${ newFieldID }]` ); newFieldOptions = newFieldOptions.replace( regex.dataFieldID, `data-field-id="${ newFieldID }"` ); newFieldOptions = newFieldOptions.replace( regex.referenceID, `data-reference="${ newFieldID }"` ); newFieldOptions = newFieldOptions.replace( regex.elementID, regex.elementIdReplace ); // Hide all field options panels. $visibleOptions.hide(); // Add a new field options panel. $fieldOptions.after( `
    ${ newFieldOptions }
    ` ); // Get a new field options panel. const $newFieldOptions = $( `#wpforms-field-option-${ newFieldID }` ); // If the user duplicates an active field. if ( $fieldActive.data( 'field-id' ) === id && $visibleTab.length ) { // The following will help identify which tab from the sidebar panel settings is currently being viewed, // i.e., "General," "Advanced," "Smart Logic," etc. const visibleTabClassName = $visibleTab.attr( 'class' ).match( /wpforms-field-option-group-\S*/i )[ 0 ]; const $newFieldOptionsTab = $newFieldOptions.find( `>.${ visibleTabClassName }` ); // Remove any left-over state from previously duplicated options. $newFieldOptions.find( '>' ).removeClass( 'active' ); // Set active tab to the same tab that was active before the duplication. $newFieldOptionsTab.addClass( 'active' ); } // If the user duplicates an inactive field. if ( $fieldActive.data( 'field-id' ) !== id && $visibleTab.length ) { // Remove active class from the current active tab. $newFieldOptions.find( '>' ).removeClass( 'active' ); // Set active tab to "General". $newFieldOptions.find( '>.wpforms-field-option-group-basic' ).addClass( 'active' ); } // Copy over values. $fieldOptions.find( ':input' ).each( function( index, el ) { // eslint-disable-line complexity, no-unused-vars const $this = $( this ), name = $this.attr( 'name' ); if ( ! name ) { return 'continue'; } const newName = name.replace( regex.fieldID, `fields[${ newFieldID }]` ), type = $this.attr( 'type' ); if ( type === 'checkbox' || type === 'radio' ) { if ( $this.is( ':checked' ) ) { $newFieldOptions.find( `[name="${ newName }"]` ) .prop( 'checked', true ) .attr( 'checked', 'checked' ); } else { $newFieldOptions.find( `[name="${ newName }"]` ) .prop( 'checked', false ) .attr( 'checked', false ); } return; } if ( $this.is( 'select' ) ) { if ( $this.find( 'option:selected' ).length ) { const optionVal = $this.find( 'option:selected' ).val(); $newFieldOptions.find( `[name="${ newName }"]` ) .find( `[value="${ optionVal }"]` ) .prop( 'selected', true ); } return; } const value = $this.val(); if ( value === '' && $this.hasClass( 'wpforms-money-input' ) ) { $newFieldOptions.find( `[name="${ newName }"]` ).val( wpf.numberFormat( '0', wpforms_builder.currency_decimals, wpforms_builder.currency_decimal, wpforms_builder.currency_thousands ) ); } else { // We've removed the empty value check here. // If we are duplicating a field with no value, we should respect that. $newFieldOptions.find( `[name="${ newName }"]` ).val( value ); } } ); // ID adjustments. $newFieldOptions.find( '.wpforms-field-option-hidden-id' ).val( newFieldID ); elements.$nextFieldId.val( nextID ); const $newFieldLabel = type === 'html' ? $( `#wpforms-field-option-${ newFieldID }-name` ) : $( `#wpforms-field-option-${ newFieldID }-label` ); // Adjust the label to indicate this is a copy. if ( changeLabel ) { $newFieldLabel.val( newFieldLabel ).trigger( 'input' ); } // Fire field adds custom event. $builder.trigger( 'wpformsFieldAdd', [ newFieldID, type ] ); // Re-init tooltips for a new field options panel. wpf.initTooltips(); // Re-init Modern Dropdown. if ( isModernDropdown ) { app.dropdownField.helpers.convertClassicToModern( id ); app.dropdownField.helpers.convertClassicToModern( newFieldID ); } // Re-init instance in choices related fields. app.fieldChoiceUpdate( $newField.data( 'field-type' ), newFieldID ); // Re-init color pickers. app.loadColorPickers(); return newFieldID; }, /** * Add the entry preview field before the page break field. * * @since 1.6.9 * * @param {Object} options Field options. * @param {number} position The field position. */ addEntryPreviewAndPageBreakFields( options, position ) { app.fieldAdd( 'entry-preview', options ).done( function( res ) { const entryPreviewId = res.data.field.id; app.fieldAdd( 'pagebreak', { position: position + 1 } ).done( function( res ) { app.lockEntryPreviewFieldsPosition( entryPreviewId ); const $pageBreakField = $( '#wpforms-field-' + res.data.field.id ), $nextField = $pageBreakField.nextAll( '.wpforms-field-pagebreak, .wpforms-field-entry-preview' ).first(); if ( $nextField.hasClass( 'wpforms-field-entry-preview' ) ) { app.lockEntryPreviewFieldsPosition( $nextField.data( 'field-id' ) ); } } ); } ); }, /** * Stick an entry preview field after adding. * * @since 1.6.9 * * @param {number} id ID. */ lockEntryPreviewFieldsPosition( id ) { const $entryPreviewField = $( '#wpforms-field-' + id ), $pageBreakField = $entryPreviewField.prevAll( '.wpforms-field-pagebreak:not(.wpforms-pagebreak-bottom)' ).first(), $nextPageBreakField = $entryPreviewField.nextAll( '.wpforms-field-pagebreak' ).first(), nextPageBreakFieldId = $nextPageBreakField.data( 'field-id' ), $pageBreakOptions = $( '#wpforms-field-option-' + nextPageBreakFieldId ), $pageBreakPrevToggle = $pageBreakOptions.find( '.wpforms-field-option-row-prev_toggle' ), $pageBreakPrevToggleField = $pageBreakPrevToggle.find( 'input' ); $entryPreviewField.addClass( 'wpforms-field-not-draggable' ); $pageBreakField.addClass( 'wpforms-field-not-draggable wpforms-field-entry-preview-not-deleted' ); $pageBreakPrevToggleField.prop( 'checked', 'checked' ).trigger( 'change' ); $pageBreakPrevToggle.addClass( 'wpforms-entry-preview-block' ); $( '#wpforms-add-fields-entry-preview' ).removeClass( 'wpforms-entry-preview-adding' ); $builder.trigger( 'wpformsFieldDragToggle', [ id, $entryPreviewField.data( 'field-type' ) ] ); $builder.trigger( 'wpformsFieldDragToggle', [ $pageBreakField.data( 'field-id' ), $pageBreakField.data( 'field-type' ) ] ); }, /** * An entry preview field requires a page break that locates before. * * @since 1.6.9 * * @param {jQuery} $fields List of fields in the form preview. * @param {number} position The field position. * * @return {boolean} True if we need to add a page break field before. */ isEntryPreviewFieldRequiresPageBreakBefore( $fields, position ) { const $beforeFields = $fields.slice( 0, position ).filter( '.wpforms-field-pagebreak,.wpforms-field-entry-preview' ); let needPageBreakBefore = true; if ( ! $beforeFields.length ) { return needPageBreakBefore; } $( $beforeFields.get().reverse() ).each( function() { const $this = $( this ); if ( $this.hasClass( 'wpforms-field-entry-preview' ) ) { return false; } if ( $this.hasClass( 'wpforms-field-pagebreak' ) && ! $this.hasClass( 'wpforms-field-stick' ) ) { needPageBreakBefore = false; return false; } } ); return needPageBreakBefore; }, /** * An entry preview field requires a page break that locates after. * * @since 1.6.9 * * @param {jQuery} $fields List of fields in the form preview. * @param {number} position The field position. * * @return {boolean} True if we need to add a page break field after. */ isEntryPreviewFieldRequiresPageBreakAfter( $fields, position ) { const $afterFields = $fields.slice( position ).filter( '.wpforms-field-pagebreak,.wpforms-field-entry-preview' ); let needPageBreakAfter = Boolean( $afterFields.length ); if ( ! $afterFields.length ) { return needPageBreakAfter; } $afterFields.each( function() { const $this = $( this ); if ( $this.hasClass( 'wpforms-field-entry-preview' ) ) { return false; } if ( $this.hasClass( 'wpforms-field-pagebreak' ) ) { needPageBreakAfter = false; return false; } } ); return needPageBreakAfter; }, /** * Add new field. * * @since 1.0.0 * @since 1.6.4 Added hCaptcha support. * * @param {string} type Field type. * @param {Object} options Additional options. * * @return {Promise|void} jQuery.post() promise interface. */ fieldAdd( type, options ) { // eslint-disable-line max-lines-per-function const $btn = $( `#wpforms-add-fields-${ type }` ); if ( $btn.hasClass( 'upgrade-modal' ) || $btn.hasClass( 'education-modal' ) || $btn.hasClass( 'warning-modal' ) ) { return; } if ( [ 'captcha_turnstile', 'captcha_hcaptcha', 'captcha_recaptcha', 'captcha_none' ].includes( type ) ) { app.captchaUpdate(); return; } adding = true; WPForms.Admin.Builder.DragFields.disableDragAndDrop(); app.disableFormActions(); if ( app.isUncheckedEntryPreviewField( type, options ) ) { app.addEntryPreviewField( type, options ); return; } const defaults = { position: 'bottom', $sortable: 'base', placeholder: false, scroll: true, defaults: false, }; options = $.extend( {}, defaults, options ); const data = { action: 'wpforms_new_field_' + type, id: s.formID, type, defaults: options.defaults, nonce: wpforms_builder.nonce, }; return $.post( wpforms_builder.ajax_url, data, function( res ) { // eslint-disable-line complexity if ( ! res.success ) { wpf.debug( 'Add field AJAX call is unsuccessful:', res ); return; } const $baseFieldsContainer = elements.$sortableFieldsWrap, $newField = $( res.data.preview ), $newOptions = $( res.data.options ); let $fieldContainer = options.$sortable; adding = false; $newField.css( 'display', 'none' ); if ( options.placeholder ) { options.placeholder.remove(); } if ( options.$sortable === 'default' || ! options.$sortable.length ) { $fieldContainer = $baseFieldsContainer.find( '.wpforms-fields-sortable-default' ); } if ( options.$sortable === 'base' || ! $fieldContainer.length ) { $fieldContainer = $baseFieldsContainer; } const event = WPFormsUtils.triggerEvent( $builder, 'wpformsBeforeFieldAddToDOM', [ options, $newField, $newOptions, $fieldContainer ] ); // Allow callbacks on `wpformsBeforeFieldAddToDOM` to cancel adding field // by triggering `event.preventDefault()`. if ( event.isDefaultPrevented() ) { return; } // Add field to the base level of fields. // Allow callbacks on `wpformsBeforeFieldAddToDOM` to skip adding field to the base level // by setting `event.skipAddFieldToBaseLevel = true`. if ( ! event.skipAddFieldToBaseLevel ) { app.fieldAddToBaseLevel( options, $newField, $newOptions ); } $newField.fadeIn(); $builder.find( '.no-fields, .no-fields-preview' ).remove(); if ( $( '.wpforms-field-option:not(.wpforms-field-option-layout)' ).length ) { $builder.find( '.wpforms-field-submit' ).show(); } // Scroll to the added field. if ( options.scroll && options.position.length ) { app.scrollPreviewToField( res.data.field.id ); } // Update next field id hidden input value. elements.$nextFieldId.val( res.data.field.id + 1 ); wpf.initTooltips(); app.loadColorPickers(); app.toggleAllOptionGroups(); $builder.trigger( 'wpformsFieldAdd', [ res.data.field.id, type ] ); } ).fail( function( xhr, textStatus, e ) { // eslint-disable-line no-unused-vars adding = false; wpf.debug( 'Add field AJAX call failed:', xhr.responseText ); } ).always( function() { if ( ! adding ) { WPForms.Admin.Builder.DragFields.enableDragAndDrop(); app.enableFormActions(); } } ); }, /** * Add new field to the base level of fields. * * @since 1.7.7 * * @param {Object} options Field add additional options. * @param {jQuery} $newField New field preview object. * @param {jQuery} $newOptions New field options object. */ fieldAddToBaseLevel( options, $newField, $newOptions ) { // eslint-disable-line complexity const $baseFieldsContainer = elements.$sortableFieldsWrap, $baseFields = $baseFieldsContainer.children( ':not(.wpforms-field-drag-pending, .no-fields-preview)' ), totalBaseFields = $baseFields.length; const $fieldOptions = elements.$fieldOptions; if ( options.position === 'top' ) { // Add field to the top of base level fields. $baseFieldsContainer.prepend( $newField ); $fieldOptions.prepend( $newOptions ); return; } const $lastBaseField = $baseFields.last(); if ( options.position === 'bottom' && ( ! $lastBaseField.length || ! $lastBaseField.hasClass( 'wpforms-field-stick' ) ) ) { // Add field to the bottom of base level fields. $baseFieldsContainer.append( $newField ); $fieldOptions.append( $newOptions ); return; } if ( options.position === 'bottom' ) { options.position = totalBaseFields; } if ( options.position === totalBaseFields && $lastBaseField.length && $lastBaseField.hasClass( 'wpforms-field-stick' ) ) { const lastBaseFieldId = $lastBaseField.data( 'field-id' ); // Check to see if the last field we have is configured to // be stuck to the bottom, if so add the field above it. $lastBaseField.before( $newField ); $fieldOptions.find( `#wpforms-field-option-${ lastBaseFieldId }` ).before( $newOptions ); return; } const $fieldInPosition = $baseFields.eq( options.position ); if ( $fieldInPosition.length ) { const fieldInPositionId = $fieldInPosition.data( 'field-id' ); // Add field to a specific location. $fieldInPosition.before( $newField ); $fieldOptions.find( `#wpforms-field-option-${ fieldInPositionId }` ).before( $newOptions ); return; } // Something is wrong. Add the field. This should never occur. $baseFieldsContainer.append( $newField ); $fieldOptions.append( $newOptions ); }, /** * Scroll the preview panel to the desired field. * * @since 1.7.7 * * @param {number} fieldId Field ID. */ scrollPreviewToField( fieldId ) { const $field = $( `#wpforms-field-${ fieldId }` ), scrollTop = elements.$fieldsPreviewWrap.scrollTop(), $layoutField = $field.closest( '.wpforms-field-layout' ); let fieldPosition = $field.position().top; if ( $layoutField.length ) { fieldPosition = $layoutField.position().top + fieldPosition + 20; } const scrollAmount = fieldPosition > scrollTop ? fieldPosition - scrollTop : fieldPosition + scrollTop; elements.$fieldsPreviewWrap.scrollTop( scrollAmount ); }, /** * Update CAPTCHA form setting. * * @since 1.6.4 * * @return {Object} jqXHR. */ captchaUpdate() { const data = { action: 'wpforms_update_field_captcha', id: s.formID, nonce: wpforms_builder.nonce, }; return $.post( wpforms_builder.ajax_url, data, function( res ) { if ( res.success ) { const args = { title: false, content: false, icon: 'fa fa-exclamation-circle', type: 'orange', boxWidth: '450px', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, }, $enableCheckbox = $( '#wpforms-panel-field-settings-recaptcha' ); let caseName = res.data.current; $enableCheckbox.data( 'provider', res.data.provider ); // Possible cases: // // not_configured - IF CAPTCHA is not configured in the WPForms plugin settings // configured_not_enabled - IF CAPTCHA is configured in WPForms plugin settings, but wasn't set in form settings // configured_enabled - IF CAPTCHA is configured in WPForms plugin and form settings if ( 'configured_not_enabled' === caseName || 'configured_enabled' === caseName ) { // Get a correct case name. caseName = $enableCheckbox.prop( 'checked' ) ? 'configured_enabled' : 'configured_not_enabled'; // Check/uncheck a `CAPTCHA` checkbox in form setting. args.buttons.confirm.action = function() { $enableCheckbox.prop( 'checked', ( 'configured_not_enabled' === caseName ) ).trigger( 'change' ); }; } args.title = res.data.cases[ caseName ].title; args.content = res.data.cases[ caseName ].content; // Do you need a Cancel button? if ( res.data.cases[ caseName ].cancel ) { args.buttons.cancel = { text: wpforms_builder.cancel, keys: [ 'esc' ], }; } // Call a Confirm modal. $.confirm( args ); } else { // eslint-disable-next-line no-console console.log( res ); } } ).fail( function( xhr, textStatus, e ) { // eslint-disable-line no-unused-vars // eslint-disable-next-line no-console console.log( xhr.responseText ); } ); }, /** * Disable drag & drop. * * @since 1.7.1 * @since 1.7.7 Deprecated. * * @deprecated Use `WPForms.Admin.Builder.DragFields.disableDragAndDrop()` instead. */ disableDragAndDrop() { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "WPFormsBuilder.disableDragAndDrop()" has been deprecated, please use the new "WPForms.Admin.Builder.DragFields.disableDragAndDrop()" function instead!' ); WPForms.Admin.Builder.DragFields.disableDragAndDrop(); }, /** * Enable drag & drop. * * @since 1.7.1 * @since 1.7.7 Deprecated. * * @deprecated Use `WPForms.Admin.Builder.DragFields.enableDragAndDrop()` instead. */ enableDragAndDrop() { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "WPFormsBuilder.enableDragAndDrop()" has been deprecated, please use the new "WPForms.Admin.Builder.DragFields.enableDragAndDrop()" function instead!' ); WPForms.Admin.Builder.DragFields.enableDragAndDrop(); }, /** * Disable Preview, Embed, Save form actions and Form Builder exit button. * * @since 1.7.4 */ disableFormActions() { $.each( [ elements.$previewButton, elements.$embedButton, elements.$saveButton, elements.$exitButton, ], function( _index, button ) { button.prop( 'disabled', true ).addClass( 'wpforms-disabled' ); } ); }, /** * Enable Preview, Embed, Save form actions and Form Builder exit button. * * @since 1.7.4 */ enableFormActions() { $.each( [ elements.$previewButton, elements.$embedButton, elements.$saveButton, elements.$exitButton, ], function( _index, button ) { button.prop( 'disabled', false ).removeClass( 'wpforms-disabled' ); } ); }, /** * Sortable fields in the builder form preview area. * * @since 1.0.0 * @since 1.7.7 Deprecated. * * @deprecated Use `WPForms.Admin.Builder.DragFields.initSortableFields()` instead. */ fieldSortable() { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "WPFormsBuilder.fieldSortable()" has been deprecated, please use the new "WPForms.Admin.Builder.DragFields.initSortableFields()" function instead!' ); WPForms.Admin.Builder.DragFields.initSortableFields(); }, /** * Show popup in case if field is not draggable, and cancel moving. * * @since 1.7.5 * @since 1.7.6 The showPopUp parameter added. * @since 1.7.7 Deprecated. * * @deprecated Use `WPForms.Admin.Builder.DragFields.fieldDragDisable()` instead. * * @param {jQuery} $field A field or list of fields. * @param {boolean} showPopUp Whether the pop-up should be displayed on dragging attempt. */ fieldDragDisable( $field, showPopUp = true ) { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "WPFormsBuilder.fieldDragDisable()" has been deprecated, please use the new "WPForms.Admin.Builder.DragFields.fieldDragDisable()" function instead!' ); WPForms.Admin.Builder.DragFields.fieldDragDisable( $field, showPopUp ); }, /** * Allow field dragging. * * @since 1.7.5 * @since 1.7.7 Deprecated. * * @deprecated Use `WPForms.Admin.Builder.DragFields.fieldDragEnable()` instead. * * @param {jQuery} $field A field or list of fields. */ fieldDragEnable( $field ) { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "WPFormsBuilder.fieldDragEnable()" has been deprecated, please use the new "WPForms.Admin.Builder.DragFields.fieldDragEnable()" function instead!' ); WPForms.Admin.Builder.DragFields.fieldDragEnable( $field ); }, /** * Add new field choice. * * @since 1.0.0 * * @param {Event} event Event. * @param {Element} el Element. */ fieldChoiceAdd( event, el ) { event.preventDefault(); const $this = $( el ), $parent = $this.parent(), checked = $parent.find( 'input.default' ).is( ':checked' ), fieldID = $this.closest( '.wpforms-field-option-row-choices' ).data( 'field-id' ); let id = $parent.parent().attr( 'data-next-id' ); const type = $parent.parent().data( 'field-type' ), $choice = $parent.clone().insertAfter( $parent ); $choice.attr( 'data-key', id ); $choice.find( 'input.label' ).val( '' ).attr( 'name', 'fields[' + fieldID + '][choices][' + id + '][label]' ); $choice.find( 'input.value' ).val( '' ).attr( 'name', 'fields[' + fieldID + '][choices][' + id + '][value]' ); $choice.find( '.wpforms-image-upload input.source' ).val( '' ).attr( 'name', 'fields[' + fieldID + '][choices][' + id + '][image]' ); $choice.find( '.wpforms-icon-select input.source-icon' ).val( wpforms_builder.icon_choices.default_icon ).attr( 'name', 'fields[' + fieldID + '][choices][' + id + '][icon]' ); $choice.find( '.wpforms-icon-select input.source-icon-style' ).val( wpforms_builder.icon_choices.default_icon_style ).attr( 'name', 'fields[' + fieldID + '][choices][' + id + '][icon_style]' ); $choice.find( '.wpforms-icon-select .ic-fa-preview' ).removeClass().addClass( 'ic-fa-preview ic-fa-' + wpforms_builder.icon_choices.default_icon_style + ' ic-fa-' + wpforms_builder.icon_choices.default_icon ); $choice.find( '.wpforms-icon-select .ic-fa-preview + span' ).text( wpforms_builder.icon_choices.default_icon ); $choice.find( 'input.default' ).attr( 'name', 'fields[' + fieldID + '][choices][' + id + '][default]' ).prop( 'checked', false ); $choice.find( '.preview' ).empty(); $choice.find( '.wpforms-image-upload-add' ).show(); $choice.find( '.wpforms-money-input' ).trigger( 'focusout' ); if ( checked === true ) { $parent.find( 'input.default' ).prop( 'checked', true ); } id++; $parent.parent().attr( 'data-next-id', id ); $builder.trigger( 'wpformsFieldChoiceAdd', [ fieldID ] ); app.fieldChoiceUpdate( type, fieldID ); }, /** * Delete field choice. * * @since 1.0.0 * * @param {Event} e Event. * @param {Element} el Element. */ fieldChoiceDelete( e, el ) { e.preventDefault(); const $this = $( el ), $list = $this.parent().parent(), total = $list.find( 'li' ).length, fieldData = { id: $list.data( 'field-id' ), choiceId: $this.closest( 'li' ).data( 'key' ), message: '' + wpforms_builder.delete_choice_confirm + '', trigger: false, }; $builder.trigger( 'wpformsBeforeFieldDeleteAlert', [ fieldData ] ); if ( total === 1 ) { app.fieldChoiceDeleteAlert(); } else { const deleteChoice = function() { $this.parent().remove(); app.fieldChoiceUpdate( $list.data( 'field-type' ), $list.data( 'field-id' ) ); $builder.trigger( 'wpformsFieldChoiceDelete', [ $list.data( 'field-id' ) ] ); }; if ( ! fieldData.trigger ) { deleteChoice(); return; } $.confirm( { title: false, content: fieldData.message, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action() { deleteChoice(); }, }, cancel: { text: wpforms_builder.cancel, }, }, } ); } }, /** * Field choice delete error alert. * * @since 1.6.7 */ fieldChoiceDeleteAlert() { $.alert( { title: false, content: wpforms_builder.error_choice, icon: 'fa fa-info-circle', type: 'blue', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); }, /** * Make field choices sortable. * Currently used for select, radio, and checkboxes field types. * * @since 1.0.0 * * @param {string} type Type. * @param {string|undefined} selector Selector. */ fieldChoiceSortable( type, selector = undefined ) { selector = typeof selector !== 'undefined' ? selector : '.wpforms-field-option-' + type + ' .wpforms-field-option-row-choices ul'; $( selector ).sortable( { items: 'li', axis: 'y', delay: 100, opacity: 0.6, handle: '.move', stop( e, ui ) { const id = ui.item.parent().data( 'field-id' ); app.fieldChoiceUpdate( type, id ); $builder.trigger( 'wpformsFieldChoiceMove', ui ); }, update( e, ui ) { // eslint-disable-line no-unused-vars }, } ); }, /** * Generate Choice label. Used in field preview template. * * @since 1.6.2 * * @param {Object} data Template data. * @param {number} choiceID Choice ID. * * @return {string} Label. */ fieldChoiceLabel( data, choiceID ) { // eslint-disable-line complexity const isPaymentChoice = [ 'payment-multiple', 'payment-checkbox' ].includes( data.settings.type ), isIconImageChoice = data.settings.choices_icons || data.settings.choices_images, isEmptyLabel = typeof data.settings.choices[ choiceID ].label === 'undefined' || data.settings.choices[ choiceID ].label.length === 0; // Do not set a placeholder for an empty label in Icon and Image choices except for payment fields. if ( isEmptyLabel && ! isPaymentChoice && isIconImageChoice ) { return ''; } const placeholder = isPaymentChoice ? wpforms_builder.payment_choice_empty_label_tpl : wpforms_builder.choice_empty_label_tpl; let label = ! isEmptyLabel ? wpf.sanitizeHTML( data.settings.choices[ choiceID ].label, wpforms_builder.allowed_label_html_tags ) : placeholder.replace( '{number}', choiceID ); if ( data.settings.show_price_after_labels ) { label += ' - ' + wpf.amountFormatCurrency( data.settings.choices[ choiceID ].value ); } return label; }, /** * Update field choices in the preview area for the Fields panel. * * Currently used for select, radio, and checkboxes field types. * * @param {string} type Field type. * @param {string|number} id Field ID. * @param {number} count Number of choices to show, -1 if not set. * * @since 1.0.0 */ fieldChoiceUpdate: ( type, id, count = -1 ) => { // eslint-disable-line complexity, max-lines-per-function const isDynamicChoices = app.dropdownField.helpers.isDynamicChoices( id ); if ( app.replaceChoicesWithTemplate( type, id, isDynamicChoices ) ) { return; } if ( count === -1 ) { count = app.settings.choicesLimitLong; } // Dropdown payment choices are of select type. if ( 'payment-select' === type ) { type = 'select'; } const $primary = $( '#wpforms-field-' + id + ' .primary-input' ); let newChoice = ''; if ( 'select' === type ) { if ( ! isDynamicChoices ) { newChoice = ''; $primary.find( 'option' ).not( '.placeholder' ).remove(); } } else if ( 'radio' === type || 'checkbox' === type || 'gdpr-checkbox' === type ) { type = 'gdpr-checkbox' === type ? 'checkbox' : type; $primary.find( 'li' ).remove(); newChoice = '
  • {label}
  • '; } // Building an inner content for Primary field. const $choicesList = $( '#wpforms-field-option-row-' + id + '-choices .choices-list' ), $choicesToRender = $choicesList.find( 'li' ).slice( 0, count ), hasDefaults = !! $choicesList.find( 'input.default:checked' ).length, modernSelectChoices = [], showPriceAfterLabels = $( '#wpforms-field-option-' + id + '-show_price_after_labels' ).prop( 'checked' ), isModernSelect = app.dropdownField.helpers.isModernSelect( $primary ); $choicesToRender.get().forEach( function( item ) {// eslint-disable-line complexity const $this = $( item ), value = $this.find( 'input.value' ).val(), choiceID = $this.data( 'key' ); let label = wpf.sanitizeHTML( $this.find( 'input.label' ).val().trim(), wpforms_builder.allowed_label_html_tags ), $choice; label = label !== '' ? label : wpforms_builder.choice_empty_label_tpl.replace( '{number}', choiceID ); label += ( showPriceAfterLabels && value ) ? ' - ' + wpf.amountFormatCurrency( value ) : ''; // Append a new choice. if ( ! isModernSelect ) { if ( ! isDynamicChoices ) { $choice = $( newChoice.replace( /{label}/g, label ) ); $primary.append( $choice ); } } else { modernSelectChoices.push( { value: label, label, } ); } const selected = $this.find( 'input.default' ).is( ':checked' ); if ( true === selected ) { switch ( type ) { case 'select': if ( ! isModernSelect ) { app.setClassicSelectedChoice( $choice ); } else { modernSelectChoices[ modernSelectChoices.length - 1 ].selected = true; } break; case 'radio': case 'checkbox': $choice.find( 'input' ).prop( 'checked', 'true' ); break; } } } ); if ( isModernSelect ) { const placeholderClass = $primary.prop( 'multiple' ) ? 'input.choices__input' : '.choices__inner .choices__placeholder', choicesjsInstance = app.dropdownField.helpers.getInstance( $primary ); if ( ! isDynamicChoices ) { choicesjsInstance.removeActiveItems(); } choicesjsInstance.setChoices( modernSelectChoices, 'value', 'label', true ); // Re-initialize modern dropdown to properly determine and update placeholder. app.dropdownField.helpers.update( id, isDynamicChoices ); // Hide/show a placeholder for Modern select if it has or not default choices. $primary .closest( '.choices' ) .find( placeholderClass ) .toggleClass( 'wpforms-hidden', hasDefaults ); } }, /** * Generate Choice label. Used in field preview template. * * @since 1.8.6 * * @param {string} type Field type. * @param {number} id Field ID. * @param {boolean} isDynamicChoices Whether the field has dynamic choices. * * @return {boolean} True if the template was used. */ replaceChoicesWithTemplate: ( type, id, isDynamicChoices ) => { // eslint-disable-line complexity // Radio, Checkbox, and Payment Multiple/Checkbox use _ template. if ( 'radio' !== type && 'checkbox' !== type && 'payment-multiple' !== type && 'payment-checkbox' !== type ) { return false; } const order = wpf.getChoicesOrder( id ), tmpl = wp.template( 'wpforms-field-preview-checkbox-radio-payment-multiple' ); const fieldSettings = wpf.getField( id ), slicedChoices = {}, slicedOrder = order.slice( 0, app.settings.choicesLimit ), data = { settings: fieldSettings, order: slicedOrder, type: 'radio', }; // If Icon Choices is on, get the valid color. if ( fieldSettings.choices_icons ) { // eslint-disable-next-line camelcase data.settings.choices_icons_color = app.getValidColorPickerValue( $( '#wpforms-field-option-' + id + '-choices_icons_color' ) ); } // Slice choices for preview. slicedOrder.forEach( function( entry ) { slicedChoices[ entry ] = fieldSettings.choices[ entry ]; } ); fieldSettings.choices = slicedChoices; if ( 'checkbox' === type || 'payment-checkbox' === type ) { data.type = 'checkbox'; } if ( ! isDynamicChoices ) { $( '#wpforms-field-' + id ).find( 'ul.primary-input' ).replaceWith( tmpl( data ) ); } // Toggle limit choices alert message. app.firstNChoicesAlert( id, order.length ); return true; }, /** * Set classic selected choice. * * @since 1.8.2.3 * * @param {jQuery|undefined} $choice Choice option. */ setClassicSelectedChoice( $choice ) { if ( $choice === undefined ) { return; } $choice.prop( 'selected', 'true' ); }, /** * Field choice bulk add toggling. * * @since 1.3.7 * * @param {Object} el jQuery object. */ fieldChoiceBulkAddToggle( el ) { const $this = $( el ), $label = $this.closest( 'label' ); if ( $this.hasClass( 'bulk-add-showing' ) ) { // "Import details" is showing, so hide/remove it. const $selector = $label.next( '.bulk-add-display' ); $selector.slideUp( 400, function() { $selector.remove(); } ); $this.find( 'span' ).text( wpforms_builder.bulk_add_show ); } else { let importOptions = '
    '; importOptions += '

    ' + wpforms_builder.bulk_add_heading + ' ' + wpforms_builder.bulk_add_presets_show + '

    '; importOptions += ''; importOptions += ''; importOptions += ''; importOptions += '
    '; $label.after( importOptions ); $label.next( '.bulk-add-display' ).slideDown( 400, function() { $( this ).find( 'textarea' ).trigger( 'focus' ); } ); $this.find( 'span' ).text( wpforms_builder.bulk_add_hide ); } $this.toggleClass( 'bulk-add-showing' ); }, /** * Field choice bulk insert the new choices. * * @since 1.3.7 * * @param {Object} el DOM element. */ fieldChoiceBulkAddInsert( el ) { const $this = $( el ), $container = $this.closest( '.wpforms-field-option-row' ), $textarea = $container.find( 'textarea' ), $list = $container.find( '.choices-list' ), $choice = $list.find( 'li:first-of-type' ).clone().wrap( '
    ' ).parent(); let choice = ''; const fieldID = $container.data( 'field-id' ), type = $list.data( 'field-type' ); let nextID = Number( $list.attr( 'data-next-id' ) ); const newValues = $textarea.val().split( '\n' ); let newChoices = ''; $this.prop( 'disabled', true ).html( $this.html() + ' ' + s.spinner ); $choice.find( 'input.value,input.label' ).attr( 'value', '' ); $choice.find( 'input.default' ).attr( 'checked', false ); $choice.find( 'input.source-icon' ).attr( 'value', wpforms_builder.icon_choices.default_icon ); $choice.find( 'input.source-icon-style' ).attr( 'value', wpforms_builder.icon_choices.default_icon_style ); $choice.find( '.ic-fa-preview' ).removeClass().addClass( `ic-fa-preview ic-fa-${ wpforms_builder.icon_choices.default_icon_style } ic-fa-${ wpforms_builder.icon_choices.default_icon }` ); $choice.find( '.ic-fa-preview + span' ).text( wpforms_builder.icon_choices.default_icon ); choice = $choice.html(); for ( const key in newValues ) { if ( ! newValues.hasOwnProperty( key ) ) { continue; } const value = wpf.sanitizeHTML( newValues[ key ] ).trim().replace( /"/g, '"' ); let newChoice = choice; newChoice = newChoice.replace( /\[choices\]\[(\d+)\]/g, '[choices][' + nextID + ']' ); newChoice = newChoice.replace( /data-key="(\d+)"/g, 'data-key="' + nextID + '"' ); newChoice = newChoice.replace( /value="" class="label"/g, 'value="' + value + '" class="label"' ); // For some reason, IE has its own attribute order. newChoice = newChoice.replace( /class="label" type="text" value=""/g, 'class="label" type="text" value="' + value + '"' ); newChoices += newChoice; nextID++; } $list.attr( 'data-next-id', nextID ).append( newChoices ); app.fieldChoiceUpdate( type, fieldID, nextID ); $builder.trigger( 'wpformsFieldChoiceAdd' ); app.fieldChoiceBulkAddToggle( $container.find( '.toggle-bulk-add-display' ) ); }, /** * Trigger $builder event. * * @since 1.9.1 * * @param {string} event Event name. */ triggerBuilderEvent( event ) { $builder.trigger( event ); }, /** * Toggle fields tabs (Add Fields, Field Options). * * @since 1.0.0 * * @param {number|string} id Field Id or `add-fields` or `field-options`. * * @return {false|void} False if event is prevented. */ fieldTabToggle( id ) { const event = WPFormsUtils.triggerEvent( $builder, 'wpformsFieldTabToggle', [ id ] ); // Allow callbacks on `wpformsFieldTabToggle` to cancel tab toggle by triggering `event.preventDefault()`. if ( event.isDefaultPrevented() ) { return false; } $( '.wpforms-tab a' ).removeClass( 'active' ); $( '.wpforms-field, .wpforms-title-desc' ).removeClass( 'active' ); if ( id === 'add-fields' ) { elements.$addFieldsTab.addClass( 'active' ); $( '.wpforms-field-options' ).hide(); $( '.wpforms-add-fields' ).show(); } else { $( '#field-options a' ).addClass( 'active' ); if ( id === 'field-options' ) { const $field = $( '.wpforms-field' ).first(); $field.addClass( 'active' ); id = $field.data( 'field-id' ); } else { $( '#wpforms-field-' + id ).addClass( 'active' ); } $( '.wpforms-field-option' ).hide(); $( '#wpforms-field-option-' + id ).show(); $( '.wpforms-add-fields' ).hide(); $( '.wpforms-field-options' ).show(); $builder.trigger( 'wpformsFieldOptionTabToggle', [ id ] ); } }, /** * Watches fields being added and listens for a pagebreak field. * * If a pagebreak field is added, and it's the first one, then we * automatically add the top and bottom pagebreak elements to the * builder. * * @param {Object} event Current DOM event. * @param {number} id Field ID. * @param {string} type Field type. * * @since 1.2.1 */ fieldPagebreakAdd( event, id, type ) { /* eslint-disable camelcase */ if ( 'pagebreak' !== type ) { return; } let options; if ( ! s.pagebreakTop ) { s.pagebreakTop = true; options = { position: 'top', scroll: false, defaults: { position: 'top', nav_align: 'left', }, }; app.fieldAdd( 'pagebreak', options ).done( function( res ) { s.pagebreakTop = res.data.field.id; const $preview = $( '#wpforms-field-' + res.data.field.id ), $options = $( '#wpforms-field-option-' + res.data.field.id ); $options.find( '.wpforms-field-option-group' ).addClass( 'wpforms-pagebreak-top' ); $preview.addClass( 'wpforms-field-stick wpforms-pagebreak-top' ); } ); } else if ( ! s.pagebreakBottom ) { s.pagebreakBottom = true; options = { position: 'bottom', scroll: false, defaults: { position: 'bottom', }, }; app.fieldAdd( 'pagebreak', options ).done( function( res ) { s.pagebreakBottom = res.data.field.id; const $preview = $( '#wpforms-field-' + res.data.field.id ), $options = $( '#wpforms-field-option-' + res.data.field.id ); $options.find( '.wpforms-field-option-group' ).addClass( 'wpforms-pagebreak-bottom' ); $preview.addClass( 'wpforms-field-stick wpforms-pagebreak-bottom' ); } ); } }, /** * Watches fields being deleted and listens for a pagebreak field. * * If a pagebreak field is added, and it's the first one, then we * automatically add the top and bottom pagebreak elements to the * builder. * * @param {Object} event Current DOM event. * @param {number} id Field ID. * @param {string} type Field type. * * @since 1.2.1 */ fieldPagebreakDelete( event, id, type ) { if ( 'pagebreak' !== type ) { return; } const pagebreaksRemaining = $( '#wpforms-panel-fields .wpforms-field-pagebreak' ).not( '.wpforms-pagebreak-top, .wpforms-pagebreak-bottom' ).length; if ( pagebreaksRemaining ) { return; } // All pagebreaks, excluding top/bottom, are gone. // So we need to remove the top and bottom pagebreak. const $preview = $( '#wpforms-panel-fields .wpforms-preview-wrap' ), $top = $preview.find( '.wpforms-pagebreak-top' ), topID = $top.data( 'field-id' ), $bottom = $preview.find( '.wpforms-pagebreak-bottom' ), bottomID = $bottom.data( 'field-id' ); $top.remove(); $( '#wpforms-field-option-' + topID ).remove(); s.pagebreakTop = false; $bottom.remove(); $( '#wpforms-field-option-' + bottomID ).remove(); s.pagebreakBottom = false; }, /** * Init Display Previous option for Pagebreak field. * * @since 1.5.8 * * @param {jQuery} $field Page Break field jQuery object. */ fieldPageBreakInitDisplayPrevious( $field ) { const id = $field.data( 'field-id' ), $prevToggle = $( '#wpforms-field-option-row-' + id + '-prev_toggle' ), $prev = $( '#wpforms-field-option-row-' + id + '-prev' ), $prevBtn = $field.find( '.wpforms-pagebreak-prev' ); if ( $field.prevAll( '.wpforms-field-pagebreak.wpforms-pagebreak-normal' ).length > 0 ) { $prevToggle.removeClass( 'hidden' ); $prev.removeClass( 'hidden' ); if ( $prevToggle.find( 'input' ).is( ':checked' ) ) { $prevBtn.removeClass( 'wpforms-hidden' ).text( $prev.find( 'input' ).val() ); } } else { $prevToggle.addClass( 'hidden' ); $prev.addClass( 'hidden' ); $prevBtn.addClass( 'wpforms-hidden' ); } }, /** * Field Dynamic Choice toggle. * * @since 1.2.8 * * @param {Element} el Element. */ fieldDynamicChoiceToggle( el ) { // eslint-disable-line complexity, max-lines-per-function let optionHTML; const $this = $( el ), $thisOption = $this.parent(), value = $this.val(), id = $thisOption.data( 'field-id' ); const $choices = $( '#wpforms-field-option-row-' + id + '-choices' ), $images = $( '#wpforms-field-option-' + id + '-choices_images' ), $icons = $( '#wpforms-field-option-' + id + '-choices_icons' ), $basicOptions = $( `#wpforms-field-option-basic-${ id }` ); // Hide image and icon choices if "dynamic choices" is not off. app.fieldDynamicChoiceToggleImageChoices(); app.fieldDynamicChoiceToggleIconChoices(); // Fire an event when a field's dynamic choices option was changed. $builder.trigger( 'wpformsFieldDynamicChoiceToggle', [ id ] ); // Loading wpf.fieldOptionLoading( $thisOption ); // Remove previous dynamic post type or taxonomy source options. $( '#wpforms-field-option-row-' + id + '-dynamic_post_type' ).remove(); $( '#wpforms-field-option-row-' + id + '-dynamic_taxonomy' ).remove(); /* * Post type- or Taxonomy-based dynamic populating. */ if ( '' !== value ) { // Hide choice images and icons options, not applicable. $images.addClass( 'wpforms-hidden' ); $icons.addClass( 'wpforms-hidden' ); // Hide `Bulk Add` toggle. $choices.find( '.toggle-bulk-add-display' ).addClass( 'wpforms-hidden' ); // Hide AI Choices button. $basicOptions.find( '.wpforms-ai-choices-button' ).addClass( 'wpforms-hidden' ); // Hide tooltip. $choices.find( '.wpforms-help-tooltip' ).addClass( 'wpforms-hidden' ); const data = { type: value, field_id: id, // eslint-disable-line camelcase action: 'wpforms_builder_dynamic_choices', nonce: wpforms_builder.nonce, }; $.post( wpforms_builder.ajax_url, data, function( res ) { if ( res.success ) { // New option markup. $thisOption.after( res.data.markup ); } else { // eslint-disable-next-line no-console console.log( res ); } // Hide loading indicator. wpf.fieldOptionLoading( $thisOption, true ); // Re-init tooltips for new field. wpf.initTooltips(); // Trigger Dynamic source updates. const $dynamicValue = $( '#wpforms-field-option-' + id + '-dynamic_' + value ); $dynamicValue.find( 'option' ).first().prop( 'selected', true ); $dynamicValue.trigger( 'change' ); } ).fail( function( xhr, textStatus, e ) { // eslint-disable-line no-unused-vars // eslint-disable-next-line no-console console.log( xhr.responseText ); } ); return; // Nothing more for dynamic populating. } /* * "Off" - no dynamic populating. */ let type = $( '#wpforms-field-option-' + id ).find( '.wpforms-field-option-hidden-type' ).val(); // Show choice images and icon options. $images.removeClass( 'wpforms-hidden' ); $icons.removeClass( 'wpforms-hidden' ); // Show `Bulk Add` toggle. $choices.find( '.toggle-bulk-add-display' ).removeClass( 'wpforms-hidden' ); // Show AI Choices button. $basicOptions.find( '.wpforms-ai-choices-button' ).removeClass( 'wpforms-hidden' ); // Show tooltip. $choices.find( '.wpforms-help-tooltip' ).removeClass( 'wpforms-hidden' ); const $wpformsField = $( '#wpforms-field-' + id ); $wpformsField.find( '.wpforms-alert' ).remove(); if ( [ 'checkbox', 'radio', 'payment-multiple', 'payment-checkbox' ].indexOf( type ) > -1 ) { app.fieldChoiceUpdate( type, id ); // Toggle elements and hide loading indicator. $choices.find( 'ul' ).removeClass( 'wpforms-hidden' ); $choices.find( '.wpforms-alert' ).addClass( 'wpforms-hidden' ); wpf.fieldOptionLoading( $thisOption, true ); return; // Nothing more for those types. } // Get original field choices. const $field = $wpformsField; const choices = [], $primary = $field.find( '.primary-input' ); let key; $( '#wpforms-field-option-row-' + id + '-choices li' ).each( function() { const $this = $( this ); choices.push( { label: wpf.sanitizeHTML( $this.find( '.label' ).val() ), selected: $this.find( '.default' ).is( ':checked' ), } ); } ); // Restore field to display original field choices. if ( $field.hasClass( 'wpforms-field-select' ) ) { const isModernSelect = app.dropdownField.helpers.isModernSelect( $primary ); let selected = false; // Remove previous items. $primary.find( 'option' ).not( '.placeholder' ).remove(); // Update Modern Dropdown. if ( isModernSelect && choices.length ) { app.dropdownField.helpers.update( id, false ); } else { // Update Classic select field. for ( key in choices ) { selected = choices[ key ].selected; optionHTML = '' : '>'; optionHTML += choices[ key ].label + ''; $primary.append( optionHTML ); } } } else { type = 'radio'; if ( $field.hasClass( 'wpforms-field-checkbox' ) ) { type = 'checkbox'; } // Remove previous items. $primary.empty(); // Add new items to radio or checkbox field. for ( key in choices ) { optionHTML = '
  • ' : '>'; optionHTML += choices[ key ].label + '
  • '; $primary.append( optionHTML ); } } // Toggle elements and hide loading indicator. $choices.find( 'ul' ).removeClass( 'wpforms-hidden' ); $choices.find( '.wpforms-alert' ).addClass( 'wpforms-hidden' ); $primary.removeClass( 'wpforms-hidden' ); wpf.fieldOptionLoading( $thisOption, true ); }, /** * Field Dynamic Choice Source toggle. * * @since 1.2.8 * * @param {Element} el Element. */ fieldDynamicChoiceSource( el ) { // eslint-disable-line max-lines-per-function /* eslint-disable camelcase */ const $this = $( el ), $thisOption = $this.parent(), value = $this.val(), id = $thisOption.data( 'field-id' ), form_id = $( '#wpforms-builder-form' ).data( 'id' ), $choices = $( '#wpforms-field-option-row-' + id + '-choices' ), $field = $( '#wpforms-field-' + id ), type = $( '#wpforms-field-option-' + id + '-dynamic_choices option:selected' ).val(); let limit = 20; // Loading. wpf.fieldOptionLoading( $thisOption ); const data = { type, source: value, field_id: id, form_id, action: 'wpforms_builder_dynamic_source', nonce: wpforms_builder.nonce, }; $.post( wpforms_builder.ajax_url, data, function( res ) { if ( ! res.success ) { // eslint-disable-next-line no-console console.log( res ); // Toggle elements and hide loading indicator. wpf.fieldOptionLoading( $thisOption, true ); return; } // Update info box and remove old choices. $choices.find( '.dynamic-name' ).text( res.data.source_name ); $choices.find( '.dynamic-type' ).text( res.data.type_name ); $choices.find( 'ul' ).addClass( 'wpforms-hidden' ); $choices.find( '.wpforms-alert' ).removeClass( 'wpforms-hidden' ); // Update items. app.fieldDynamicChoiceSourceItems( $field, res.data.items ); if ( $field.hasClass( 'wpforms-field-select' ) ) { limit = 200; } // Remove any previous empty message. $field.find( '.wpforms-notice-dynamic-empty' ).remove(); // If the source has more items than the field type can // ideally handle alert the user. if ( Number( res.data.total ) > limit ) { let msg = wpforms_builder.dynamic_choices.limit_message; msg = msg.replace( '{source}', res.data.source_name ); msg = msg.replace( '{type}', res.data.type_name ); msg = msg.replace( '{limit}', limit ); msg = msg.replace( '{total}', res.data.total ); $.alert( { title: wpforms_builder.heads_up, content: msg, icon: 'fa fa-info-circle', type: 'blue', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); } // Toggle limit choices alert message. app.firstNChoicesAlert( id, res.data.total ); // Toggle empty choices notice message. if ( Number( res.data.total ) === 0 ) { app.emptyChoicesNotice( id, res.data.source_name, res.data.type ); } // Toggle elements and hide loading indicator. wpf.fieldOptionLoading( $thisOption, true ); } ).fail( function( xhr, textStatus, e ) { // eslint-disable-line no-unused-vars // eslint-disable-next-line no-console console.log( xhr.responseText ); } ); }, /** * Update a Field Items when `Dynamic Choice` Source is toggled. * * @since 1.6.1 * * @param {Object} $field jQuery selector for current field. * @param {Object} items Items collection. */ fieldDynamicChoiceSourceItems( $field, items ) { const $primary = $field.find( '.primary-input' ); let key = 0; if ( $field.hasClass( 'wpforms-field-select' ) ) { const isModernSelect = app.dropdownField.helpers.isModernSelect( $primary ); if ( isModernSelect ) { app.fieldDynamicChoiceSourceForModernSelect( $primary, items ); } else { app.fieldDynamicChoiceSourceForClassicSelect( $primary, items ); } } else { let type = 'radio'; if ( $field.hasClass( 'wpforms-field-checkbox' ) ) { type = 'checkbox'; } // Remove previous items. $primary.empty(); // Add new items to radio or checkbox field. for ( key in items ) { $primary.append( '
  • ' + wpf.sanitizeHTML( items[ key ] ) + '
  • ' ); } } }, /** * Update options for Modern style select when `Dynamic Choice` Source is toggled. * * @since 1.6.1 * * @param {Object} $jquerySelector jQuery selector for primary input. * @param {Object} items Items collection. */ fieldDynamicChoiceSourceForModernSelect( $jquerySelector, items ) { const instance = app.dropdownField.helpers.getInstance( $jquerySelector ), fieldId = $jquerySelector.closest( '.wpforms-field' ).data().fieldId; // Destroy the instance of Choices.js. instance.destroy(); // Update a placeholder. app.dropdownField.helpers.updatePlaceholderChoice( instance, fieldId ); // Update options. app.fieldDynamicChoiceSourceForClassicSelect( $jquerySelector, items ); // Choices.js init. app.dropdownField.events.choicesInit( $jquerySelector ); }, /** * Update options for Classic style select when `Dynamic Choice` Source is toggled. * * @since 1.6.1 * * @param {Object} $jquerySelector jQuery selector for primary input. * @param {Object} items Items collection. */ fieldDynamicChoiceSourceForClassicSelect( $jquerySelector, items ) { let index = 0; const itemsSize = items.length; // Clear. $jquerySelector.find( 'option' ).not( '.placeholder' ).remove(); // Add options (items) to a single ', nameError = '

    ' + wpforms_builder[ blockType + '_error' ] + '

    ', modalContent = namePrompt + nameField + nameError; const modal = $.confirm( { container: $builder, title: false, content: modalContent, icon: 'fa fa-info-circle', type: 'blue', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action() { // eslint-disable-line complexity, max-lines-per-function const settingsBlockName = this.$content.find( 'input#settings-block-name' ).val().toString().trim(), error = this.$content.find( '.error' ); if ( settingsBlockName === '' ) { error.show(); return false; } const $firstSettingsBlock = $el.closest( '.wpforms-panel-content-section' ).find( '.wpforms-builder-settings-block' ).first(); // Restore tooltips before cloning. wpf.restoreTooltips( $firstSettingsBlock ); const $newSettingsBlock = $firstSettingsBlock.clone(), blockID = $firstSettingsBlock.data( 'block-id' ); let newSettingsBlock; $newSettingsBlock.attr( 'data-block-id', nextID ); $newSettingsBlock.find( '.wpforms-builder-settings-block-header span' ).text( settingsBlockName ); /** * Fires to reset settings block elements on adding new settings block. * * @param {jQuery} $element jQuery object of an element. */ const resetFormElement = function( $element ) { if ( $element.attr( 'name' ) ) { $element.val( '' ).attr( 'name', $element.attr( 'name' ).replace( /\[(\d+)\]/, '[' + nextID + ']' ) ); if ( $element.is( 'select' ) ) { $element.find( 'option' ).prop( 'selected', false ).attr( 'selected', false ); $element.find( 'option' ).first().prop( 'selected', true ).attr( 'selected', 'selected' ); } else if ( $element.attr( 'type' ) === 'checkbox' ) { const isChecked = $element.closest( '.wpforms-panel-field' ).hasClass( 'js-wpforms-enabled-notification' ); $element.prop( 'checked', isChecked ).attr( 'checked', isChecked ).val( '1' ); } else { $element.val( '' ).attr( 'value', '' ); } } }; $newSettingsBlock.find( 'input, textarea, select' ).each( function() { const $this = $( this ); const $parent = $this.parent(); if ( $this.hasClass( 'wpforms-disabled' ) && ( $parent.hasClass( 'from-name' ) || $parent.hasClass( 'from-email' ) ) ) { return; } resetFormElement( $this ); } ); // Update elements IDs. const idPrefixPanel = 'wpforms-panel-field-' + panelID + '-', idPrefixBlock = idPrefixPanel + blockID; $newSettingsBlock.find( '[id^="' + idPrefixBlock + '"], [for^="' + idPrefixBlock + '"]' ).each( function( index, el ) { // eslint-disable-line no-unused-vars const $el = $( this ), attr = $el.prop( 'tagName' ) === 'LABEL' ? 'for' : 'id', elID = $el.attr( attr ).replace( new RegExp( idPrefixBlock, 'g' ), idPrefixPanel + nextID ); $el.attr( attr, elID ); } ); // Update `notification by status` checkboxes. const radioGroup = blockID + '-notification-by-status'; $newSettingsBlock.find( '[data-radio-group="' + radioGroup + '"]' ).each( function( index, el ) { // eslint-disable-line no-unused-vars $( this ) .removeClass( 'wpforms-radio-group-' + radioGroup ) .addClass( 'wpforms-radio-group-' + nextID + '-notification-by-status' ) .attr( 'data-radio-group', nextID + '-notification-by-status' ); } ); $newSettingsBlock.find( '.wpforms-builder-settings-block-header input' ).val( settingsBlockName ).attr( 'value', settingsBlockName ); if ( blockType === 'notification' ) { $newSettingsBlock.find( '.email-msg textarea' ).val( '{all_fields}' ).attr( 'value', '{all_fields}' ); $newSettingsBlock.find( '.email-recipient input' ).val( '{admin_email}' ).attr( 'value', '{admin_email}' ); } $newSettingsBlock.removeClass( 'wpforms-builder-settings-block-default' ); if ( blockType === 'confirmation' ) { $newSettingsBlock.find( '.wpforms-panel-field-tinymce' ).remove(); if ( typeof WPForms !== 'undefined' ) { $newSettingsBlock.find( '.wpforms-panel-field-confirmations-type-wrap' ) .after( WPForms.Admin.Builder.Templates .get( 'wpforms-builder-confirmations-message-field' )( { id: nextID, } ) ); } } // Conditional logic, if present const $conditionalLogic = $newSettingsBlock.find( '.wpforms-conditional-block' ); if ( $conditionalLogic.length && typeof WPForms !== 'undefined' ) { $conditionalLogic .html( WPForms.Admin.Builder.Templates .get( 'wpforms-builder-conditional-logic-toggle-field' )( { id: nextID, type: blockType, actions: JSON.stringify( $newSettingsBlock.find( '.wpforms-panel-field-conditional_logic-checkbox' ).data( 'actions' ) ), actionDesc: $newSettingsBlock.find( '.wpforms-panel-field-conditional_logic-checkbox' ).data( 'action-desc' ), reference: $newSettingsBlock.find( '.wpforms-panel-field-conditional_logic-checkbox' ).data( 'reference' ), } ) ); } // Fields Map Table, if present. const $fieldsMapTable = $newSettingsBlock.find( '.wpforms-field-map-table' ); if ( $fieldsMapTable.length ) { $fieldsMapTable.each( function( index, el ) { const $table = $( el ); // Clean table fields. $table.find( 'tr:not(:first-child)' ).remove(); const $input = $table.find( '.key input' ), $select = $table.find( '.field select' ), name = $select.data( 'name' ); $input.attr( 'value', '' ); $select .attr( 'name', '' ) .attr( 'data-name', name.replace( /\[(\d+)\]/, '[' + nextID + ']' ) ); } ); } newSettingsBlock = $newSettingsBlock.wrap( '
    ' ).parent().html(); newSettingsBlock = newSettingsBlock.replace( /\[conditionals\]\[(\d+)\]\[(\d+)\]/g, '[conditionals][0][0]' ); $firstSettingsBlock.before( newSettingsBlock ); const $addedSettingBlock = $firstSettingsBlock.prev(); // Reset the confirmation type to the 1st one. if ( blockType === 'confirmation' ) { app.prepareChoicesJSField( $addedSettingBlock, nextID ); app.confirmationFieldsToggle( $( '.wpforms-panel-field-confirmations-type' ).first() ); } // Init the WP Editor. if ( typeof tinymce !== 'undefined' && typeof wp.editor !== 'undefined' && blockType === 'confirmation' ) { wp.editor.initialize( 'wpforms-panel-field-confirmations-message-' + nextID, s.tinymceDefaults ); } // Init tooltips for a new section. wpf.initTooltips(); $builder.trigger( 'wpformsSettingsBlockAdded', [ $addedSettingBlock ] ); $el.attr( 'data-next-id', nextID + 1 ); }, }, cancel: { text: wpforms_builder.cancel, }, }, } ); // We need to process this event here, because we need a confirmation // modal object defined, so we can intrude into it. // Pressing Enter will click the Ok button. $builder.on( 'keypress', '#settings-block-name', function( e ) { if ( e.keyCode === 13 ) { $( modal.buttons.confirm.el ).trigger( 'click' ); } } ); }, /** * Reset the 'Select Page' field to it's initial state then * re-initialize ChoicesJS on it. * * @since 1.7.9 * * @param {jQuery} $addedSettingBlock Newly added Settings Block jQuery object. * @param {number} addedSettingBlockID Number ID used when `$addedSettingBlock` was created. */ prepareChoicesJSField( $addedSettingBlock, addedSettingBlockID ) { const $addedConfirmationWrap = $addedSettingBlock.find( `#wpforms-panel-field-confirmations-${ addedSettingBlockID }-page-wrap` ); if ( $addedConfirmationWrap.length <= 0 ) { return; } const $confirmationSelectPageField = $addedConfirmationWrap.find( `#wpforms-panel-field-confirmations-${ addedSettingBlockID }-page` ); if ( $confirmationSelectPageField.length <= 0 && ! $confirmationSelectPageField.hasClass( 'choicesjs-select' ) ) { return; } const $choicesWrapper = $addedConfirmationWrap.find( '.choices' ); if ( $choicesWrapper.length <= 0 ) { return; } // Remove ChoicesJS-related attr. const $selectPageField = $confirmationSelectPageField.first(); $selectPageField.removeAttr( 'data-choice' ); $selectPageField.removeAttr( 'hidden' ); $selectPageField.removeClass( 'choices__input' ); // Move the select page field to it's initial location in the DOM. $( $selectPageField ).appendTo( $addedConfirmationWrap.first() ); // Remove the `.choices` wrapper. $choicesWrapper.first().remove(); // Re-init ChoicesJS. app.dropdownField.events.choicesInit( $selectPageField ); }, /** * Show settings block editing interface. * * @since 1.4.8 * * @param {jQuery} $el Element. */ settingsBlockNameEditingShow( $el ) { const headerHolder = $el.parents( '.wpforms-builder-settings-block-header' ), nameHolder = headerHolder.find( '.wpforms-builder-settings-block-name' ); nameHolder .addClass( 'editing' ) .hide(); // Make the editing interface active and in focus headerHolder.find( '.wpforms-builder-settings-block-name-edit' ).addClass( 'active' ); wpf.focusCaretToEnd( headerHolder.find( 'input' ) ); }, /** * Update settings block name and hide editing interface. * * @since 1.4.8 * * @param {jQuery} $el Element. */ settingsBlockNameEditingHide( $el ) { const headerHolder = $el.parents( '.wpforms-builder-settings-block-header' ), nameHolder = headerHolder.find( '.wpforms-builder-settings-block-name' ), editHolder = headerHolder.find( '.wpforms-builder-settings-block-name-edit' ); let currentName = editHolder.find( 'input' ).val().trim(); const blockType = $el.closest( '.wpforms-builder-settings-block' ).data( 'block-type' ); // Provide a default value for empty settings block name. if ( ! currentName.length ) { currentName = wpforms_builder[ blockType + '_def_name' ]; } // This is done for sanitizing. editHolder.find( 'input' ).val( currentName ); nameHolder.text( currentName ); // Editing should be hidden, displaying - active. nameHolder .removeClass( 'editing' ) .show(); editHolder.removeClass( 'active' ); }, /** * Clone the Notification block with all of its content and events. * Put the newly created clone above the target. * * @since 1.6.5 * @since 1.7.7 Registered `wpformsSettingsBlockCloned` trigger. * * @param {Object} $el Clone icon DOM element. */ settingsBlockPanelClone( $el ) { // eslint-disable-line max-lines-per-function const $panel = $el.closest( '.wpforms-panel-content-section' ), $addNewSettingButton = $panel.find( '.wpforms-builder-settings-block-add' ), $settingsBlock = $el.closest( '.wpforms-builder-settings-block' ), $settingBlockContent = $settingsBlock.find( '.wpforms-builder-settings-block-content' ), settingsBlockId = parseInt( $addNewSettingButton.attr( 'data-next-id' ), 10 ), settingsBlockType = $settingsBlock.data( 'block-type' ), settingsBlockName = $settingsBlock.find( '.wpforms-builder-settings-block-name' ).text().trim() + wpforms_builder[ settingsBlockType + '_clone' ], isVisibleContent = $settingBlockContent.is( ':hidden' ); // Restore tooltips before cloning. wpf.restoreTooltips( $settingsBlock ); const $clone = $settingsBlock.clone( false, true ); // Save open/close state while cloning. app.settingsBlockUpdateState( isVisibleContent, settingsBlockId, settingsBlockType ); // Change the cloned setting block ID and name. $clone.data( 'block-id', settingsBlockId ); $clone.find( '.wpforms-builder-settings-block-header span' ).text( settingsBlockName ); $clone.find( '.wpforms-builder-settings-block-header input' ).val( settingsBlockName ); $clone.removeClass( 'wpforms-builder-settings-block-default' ); // Change the Next Settings block ID for "Add new" button. $addNewSettingButton.attr( 'data-next-id', settingsBlockId + 1 ); // Change the name attribute. $clone.find( 'input, textarea, select' ).each( function() { const $this = $( this ); if ( $this.attr( 'name' ) ) { $this.attr( 'name', $this.attr( 'name' ).replace( /\[(\d+)\]/, '[' + settingsBlockId + ']' ) ); } if ( $this.data( 'name' ) ) { $this.data( 'name', $this.data( 'name' ).replace( /\[(\d+)\]/, '[' + settingsBlockId + ']' ) ); } if ( $this.attr( 'class' ) ) { $this.attr( 'class', $this.attr( 'class' ).replace( /-(\d+)/, '-' + settingsBlockId ) ); } if ( $this.attr( 'data-radio-group' ) ) { $this.attr( 'data-radio-group', $this.attr( 'data-radio-group' ).replace( /(\d+)-/, settingsBlockId + '-' ) ); } } ); // Change IDs/data-attributes in DOM elements. $clone.find( '*' ).each( function() { const $this = $( this ); if ( $this.attr( 'id' ) ) { $this.attr( 'id', $this.attr( 'id' ).replace( /-(\d+)/, '-' + settingsBlockId ) ); } if ( $this.attr( 'for' ) ) { $this.attr( 'for', $this.attr( 'for' ).replace( /-(\d+)-/, '-' + settingsBlockId + '-' ) ); } if ( $this.data( 'input-name' ) ) { $this.data( 'input-name', $this.data( 'input-name' ).replace( /\[(\d+)\]/, '[' + settingsBlockId + ']' ) ); } } ); // Transfer selected values to copy elements since jQuery doesn't clone the current selected state. $settingsBlock.find( 'select' ).each( function() { const baseSelectName = $( this ).attr( 'name' ), clonedSelectName = $( this ).attr( 'name' ).replace( /\[(\d+)\]/, '[' + settingsBlockId + ']' ); $clone.find( 'select[name="' + clonedSelectName + '"]' ).val( $( this ).attr( 'name', baseSelectName ).val() ); } ); // Insert before the target settings block. $clone .css( 'display', 'none' ) .insertBefore( $settingsBlock ) .show( 'fast', function() { // Init tooltips for a new section. wpf.initTooltips(); } ); $builder.trigger( 'wpformsSettingsBlockCloned', [ $clone, $settingsBlock.data( 'block-id' ) ] ); }, /** * Show or hide settings block panel content. * * @since 1.4.8 * * @param {Object} $el Toggle icon DOM element. */ settingsBlockPanelToggle( $el ) { const $settingsBlock = $el.closest( '.wpforms-builder-settings-block' ), settingsBlockId = $settingsBlock.data( 'block-id' ), settingsBlockType = $settingsBlock.data( 'block-type' ), $content = $settingsBlock.find( '.wpforms-builder-settings-block-content' ), isVisible = $content.is( ':visible' ); $content.stop().slideToggle( { duration: 400, start() { // Send it early to save fast. // It's an animation start, so we should save the state for the animation end (reversed). app.settingsBlockUpdateState( isVisible, settingsBlockId, settingsBlockType ); }, always() { if ( $content.is( ':visible' ) ) { $el.html( '' ); } else { $el.html( '' ); } }, } ); }, /** * Delete settings block. * * @since 1.4.8 * @since 1.6.1.2 Registered `wpformsSettingsBlockDeleted` trigger. * * @param {jQuery} $el Delete button element. */ settingsBlockDelete( $el ) { const $contentSection = $el.closest( '.wpforms-panel-content-section' ); // Skip if only one block persist. // This condition should not execute in normal circumstances. if ( $contentSection.find( '.wpforms-builder-settings-block' ).length < 2 ) { return; } const $currentBlock = $el.closest( '.wpforms-builder-settings-block' ), blockType = $currentBlock.data( 'block-type' ); $.confirm( { title: false, content: wpforms_builder[ blockType + '_delete' ], icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action() { const settingsBlockId = $currentBlock.data( 'block-id' ), settingsBlockType = $currentBlock.data( 'block-type' ); /* eslint-disable camelcase */ $.post( wpforms_builder.ajax_url, { action: 'wpforms_builder_settings_block_state_remove', nonce: wpforms_builder.nonce, block_id: settingsBlockId, block_type: settingsBlockType, form_id: s.formID, } ); /* eslint-enable */ $currentBlock.remove(); $builder.trigger( 'wpformsSettingsBlockDeleted', [ blockType, settingsBlockId ] ); }, }, cancel: { text: wpforms_builder.cancel, }, }, } ); }, /** * Change open/close state for setting block. * * @since 1.6.5 * * @param {boolean} isVisible State status. * @param {number} settingsBlockId Block ID. * @param {string} settingsBlockType Block type. */ settingsBlockUpdateState( isVisible, settingsBlockId, settingsBlockType ) { /* eslint-disable camelcase */ $.post( wpforms_builder.ajax_url, { action: 'wpforms_builder_settings_block_state_save', state: isVisible ? 'closed' : 'opened', form_id: s.formID, block_id: settingsBlockId, block_type: settingsBlockType, nonce: wpforms_builder.nonce, } ); }, /** * Change visibility for notification elements, e.g., * the Enable This Notification toggle and notification status. * The elements invisible when form has only one notification * and customers can turn off all notifications instead. * * @since 1.9.2 */ notificationsUpdateElementsVisibility() { const $notifications = $( '.wpforms-panel-content-section-notifications .wpforms-notification' ); const $statuses = $notifications.find( '.wpforms-builder-settings-block-status' ); const isVisible = $notifications.length > 1; const $fieldWrappers = $notifications.find( '.js-wpforms-enabled-notification' ); if ( isVisible ) { $fieldWrappers.removeClass( 'wpforms-hidden' ); $statuses.removeClass( 'wpforms-hidden' ); return; } const $inputs = $fieldWrappers.find( 'input' ); $statuses.addClass( 'wpforms-hidden' ); $fieldWrappers.addClass( 'wpforms-hidden' ); $inputs.prop( 'checked', true ); }, /** * Update notification status to display if the notification is active or inactive. * * @since 1.9.2 * * @since 1.9.2 * * @param {jQuery} $notification Notification element. */ notificationUpdateStatus( $notification ) { const isNotificationsEnabled = $( '#wpforms-panel-field-settings-notification_enable' ).is( ':checked' ); const isEnabledNotification = $notification.find( '.js-wpforms-enabled-notification input' ).is( ':checked' ); const $status = $notification.find( '.wpforms-builder-settings-block-status' ); if ( isNotificationsEnabled && isEnabledNotification ) { $status.removeClass( 'wpforms-badge-silver' ); $status.addClass( 'wpforms-badge-green' ); $status.text( $status.data( 'active' ) ); return; } $status.removeClass( 'wpforms-badge-green' ); $status.addClass( 'wpforms-badge-silver' ); $status.text( $status.data( 'inactive' ) ); }, //--------------------------------------------------------------------// // Revisions Panel //--------------------------------------------------------------------// /** * Element bindings for Revisions panel. * * @since 1.7.3 */ bindUIActionsRevisions() { // Update a revisions panel when it becomes active. $builder.on( 'wpformsPanelSwitched', function( event, panel ) { if ( panel !== 'revisions' ) { return; } app.updateRevisionsList(); app.updateRevisionPreview(); } ); // Update revisions list when the form was saved with a revisions panel being active. $builder.on( 'wpformsSaved', function( event ) { // eslint-disable-line no-unused-vars if ( wpf.getQueryString( 'view' ) !== 'revisions' ) { return; } app.updateRevisionsList(); } ); }, /** * Fetch and update a list of form revisions. * * @since 1.7.3 */ updateRevisionsList() { const $revisionsButtonBadge = $( '.wpforms-panel-revisions-button .badge-exclamation' ); // Revisions' badge exists, send a request and remove the badge on successful response. if ( $revisionsButtonBadge.length ) { $.post( wpforms_builder.ajax_url, { action: 'wpforms_mark_panel_viewed', form_id: s.formID, // eslint-disable-line camelcase nonce: wpforms_builder.nonce, } ) .done( function( response ) { // eslint-disable-next-line no-unused-expressions response.success ? $revisionsButtonBadge.remove() : wpf.debug( response ); } ) .fail( function( xhr, textStatus, e ) { // eslint-disable-line no-unused-vars wpf.debug( xhr.responseText || textStatus || '' ); } ); } // Revisions are disabled, no need to fetch a list of revisions. if ( ! $builder.hasClass( 'wpforms-revisions-enabled' ) ) { return; } const $revisionsList = $( '#wpforms-panel-revisions .wpforms-revisions-content' ); // Dim the list, send a request and replace the list on successful response. $revisionsList.fadeTo( 250, 0.25, function() { $.post( wpforms_builder.ajax_url, { action: 'wpforms_get_form_revisions', form_id: s.formID, // eslint-disable-line camelcase revision_id: wpf.getQueryString( 'revision_id' ), // eslint-disable-line camelcase nonce: wpforms_builder.nonce, } ) .done( function( response ) { // eslint-disable-next-line no-unused-expressions response.success ? $revisionsList.replaceWith( response.data.html ) : wpf.debug( response ); } ) .fail( function( xhr, textStatus, e ) { // eslint-disable-line no-unused-vars wpf.debug( xhr.responseText || textStatus || '' ); // Un dim the list to reset the UI. $revisionsList.fadeTo( 250, 1 ); } ); } ); }, /** * Clone form preview from Fields panel. * * @since 1.7.3 */ updateRevisionPreview() { // Clone preview DOM from a Fields panel. const $preview = elements.$formPreview.clone(); // Clean up the cloned preview, remove unnecessary elements, set states etc. $preview .find( '.wpforms-field-duplicate, .wpforms-field-delete, .wpforms-field-helper, .wpforms-debug' ) .remove() .end(); $preview .find( '.wpforms-field-wrap' ) .removeClass( 'ui-sortable' ) .addClass( 'ui-sortable-disabled' ); $preview .find( '.wpforms-field' ) .removeClass( 'ui-sortable-handle ui-draggable ui-draggable-handle active' ) .removeAttr( 'id data-field-id data-field-type' ) .removeData(); $preview .find( '.wpforms-field-submit-button' ) .prop( 'disabled', true ); // Put the cleaned-up clone into a Preview panel. if ( elements.$revisionPreview.hasClass( 'has-preview' ) ) { elements .$revisionPreview .find( '.wpforms-preview-wrap' ) .replaceWith( $preview ); } else { elements .$revisionPreview .append( $preview ) .addClass( 'has-preview' ); } }, /** * Inform the user about making this version the default if revision is currently loaded, and it was modified. * * @since 1.7.3 */ confirmSaveRevision() { $.confirm( { title: wpforms_builder.heads_up, content: wpforms_builder.revision_update_confirm, icon: 'fa fa-exclamation-circle', type: 'orange', closeIcon: false, buttons: { confirm: { text: wpforms_builder.save, btnClass: 'btn-confirm', keys: [ 'enter' ], action() { // Put the Form Builder into "saving state". $builder.addClass( 'wpforms-revision-is-saving' ); // Save the revision as current version and reload the Form Builder. WPFormsBuilder.formSave( false ).done( app.revisionSavedReload ); }, }, cancel: { text: wpforms_builder.cancel, action() { WPFormsBuilder.setCloseConfirmation( true ); }, }, }, } ); }, /** * When a modified revision was saved as a current version, reload the Form Builder with the current tab active. * * @since 1.7.3 */ revisionSavedReload() { wpf.updateQueryString( 'view', wpf.getQueryString( 'view' ) ); wpf.removeQueryParam( 'revision_id' ); window.location.reload(); }, //--------------------------------------------------------------------// // Save and Exit //--------------------------------------------------------------------// /** * Element bindings for Embed and Save/Exit items. * * @since 1.0.0 * @since 1.5.8 Added trigger on `wpformsSaved` event to remove a `newform` URL-parameter. */ bindUIActionsSaveExit() { // Embed form. $builder.on( 'click', '#wpforms-embed', function( e ) { e.preventDefault(); if ( $( this ).hasClass( 'wpforms-disabled' ) || $( this ).hasClass( 'wpforms-btn-light-grey-disabled' ) ) { return; } WPFormsFormEmbedWizard.openPopup(); } ); // Save form. $builder.on( 'click', '#wpforms-save', function( e ) { e.preventDefault(); app.formSave( false ); } ); // Exit builder. $builder.on( 'click', '#wpforms-exit', function( e ) { e.preventDefault(); app.formExit(); } ); // After form save. $builder.on( 'wpformsSaved', function( e, data ) { // eslint-disable-line no-unused-vars /** * Remove `newform` parameter if it's in URL, otherwise we can to get a "race condition". * E.g., form settings will be updated before some provider connection is loaded. */ wpf.removeQueryParam( 'newform' ); } ); }, // eslint-disable-next-line jsdoc/require-returns /** * Save form. * * @since 1.0.0 * @since 1.7.5 Added `wpformsBeforeSave` trigger. * * @param {boolean} redirect Whether to redirect after save. */ formSave( redirect ) { // eslint-disable-line max-lines-per-function // Saving a revision directly is not allowed. We need to notify the user that it will overwrite the current version. if ( $builder.hasClass( 'wpforms-is-revision' ) && ! $builder.hasClass( 'wpforms-revision-is-saving' ) ) { app.confirmSaveRevision(); return; } if ( typeof tinyMCE !== 'undefined' ) { tinyMCE.triggerSave(); } const event = WPFormsUtils.triggerEvent( $builder, 'wpformsBeforeSave' ); // Allow callbacks on `wpformsBeforeSave` to cancel form submission by triggering `event.preventDefault()`. if ( event.isDefaultPrevented() ) { return; } const $saveBtn = elements.$saveButton, $icon = $saveBtn.find( 'i.fa-check' ), $spinner = $saveBtn.find( 'i.wpforms-loading-spinner' ), $label = $saveBtn.find( 'span' ), text = $label.text(); $label.text( wpforms_builder.saving ); $saveBtn.prop( 'disabled', true ); $icon.addClass( 'wpforms-hidden' ); $spinner.removeClass( 'wpforms-hidden' ); const data = { action: 'wpforms_save_form', data: JSON.stringify( app.serializeAllData( $( '#wpforms-builder-form' ) ) ), id: s.formID, nonce: wpforms_builder.nonce, }; return $.post( wpforms_builder.ajax_url, data, function( response ) { if ( response.success ) { wpf.savedState = wpf.getFormState( '#wpforms-builder-form' ); wpf.initialSave = false; $builder.trigger( 'wpformsSaved', response.data ); if ( true === redirect && app.isBuilderInPopup() ) { app.builderInPopupClose( 'saved' ); return; } if ( true === redirect ) { window.location.href = wpforms_builder.exit_url; } } else { wpf.debug( response ); app.formSaveError( response.data ); } } ).fail( function( xhr, textStatus, e ) { // eslint-disable-line no-unused-vars wpf.debug( xhr ); app.formSaveError(); } ).always( function() { $label.text( text ); $saveBtn.prop( 'disabled', false ); $spinner.addClass( 'wpforms-hidden' ); $icon.removeClass( 'wpforms-hidden' ); } ); }, /** * Serialize all form data including checkboxes that are not checked. * * @since 1.9.0 * * @param {Object} $form Form jQuery object. * * @return {Array} Form data. */ serializeAllData( $form ) { const formData = $form.serializeArray(); $form.find( '.wpforms-field-option-layout .wpforms-field-option-row-label_hide input[type=checkbox]' ).each( function() { const $checkbox = $( this ); const name = $checkbox.attr( 'name' ); const value = $checkbox.is( ':checked' ) ? '1' : ''; if ( ! value ) { formData.push( { name, value } ); } } ); return formData; }, /** * Form save error. * * @since 1.6.3 * * @param {string} error Error message. */ formSaveError( error = '' ) { // Default error message. if ( wpf.empty( error ) ) { error = wpforms_builder.error_save_form; } // Display error in a modal window. $.confirm( { title: wpforms_builder.heads_up, content: '

    ' + error + '

    ' + wpforms_builder.error_contact_support + '

    ', icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); }, /** * Exit form builder. * * @since 1.0.0 */ formExit() { if ( app.isBuilderInPopup() && app.formIsSaved() ) { app.builderInPopupClose( 'saved' ); return; } if ( app.formIsSaved() ) { window.location.href = wpforms_builder.exit_url; } else { $.confirm( { title: false, content: wpforms_builder.exit_confirm, icon: 'fa fa-exclamation-circle', type: 'orange', closeIcon: true, buttons: { confirm: { text: wpforms_builder.save_exit, btnClass: 'btn-confirm', keys: [ 'enter' ], action() { app.formSave( true ); }, }, cancel: { text: wpforms_builder.exit, action() { closeConfirmation = false; if ( app.isBuilderInPopup() ) { app.builderInPopupClose( 'canceled' ); return; } window.location.href = wpforms_builder.exit_url; }, }, }, } ); } }, /** * Close confirmation setter. * * @since 1.6.2 * * @param {boolean} confirm Close confirmation flag value. */ setCloseConfirmation( confirm ) { closeConfirmation = !! confirm; }, /** * Check the current form state. * * @since 1.0.0 * * @return {boolean} True if the form is saved. */ formIsSaved() { return wpf.savedState === wpf.getFormState( '#wpforms-builder-form' ); }, /** * Check if the builder opened in the popup (iframe). * * @since 1.6.2 * * @return {boolean} True if builder opened in the popup. */ isBuilderInPopup() { return window.self !== window.parent && window.self.frameElement.id === 'wpforms-builder-iframe'; }, /** * Close popup with the form builder. * * @since 1.6.2 * * @param {string} action Performed action: saved or canceled. */ builderInPopupClose( action ) { const $popup = window.parent.jQuery( '.wpforms-builder-popup' ); const $title = $( '.wpforms-center-form-name' ).text(); $popup.find( '#wpforms-builder-iframe' ).attr( 'src', 'about:blank' ); $popup.fadeOut(); $popup.trigger( 'wpformsBuilderInPopupClose', [ action, s.formID, $title ] ); }, //--------------------------------------------------------------------// // General / global //--------------------------------------------------------------------// /** * Element bindings for general and global items. * * @since 1.2.0 */ bindUIActionsGeneral() { // eslint-disable-line max-lines-per-function // Toggle Smart Tags $builder.on( 'click', '.toggle-smart-tag-display', app.smartTagToggle ); $builder.on( 'click', '.smart-tags-list-display a', app.smartTagInsert ); // Toggle unfoldable group of fields $builder.on( 'click', '.wpforms-panel-fields-group.unfoldable .wpforms-panel-fields-group-title', app.toggleUnfoldableGroup ); // Hide field preview helper box. $builder.on( 'click', '.wpforms-field-helper-hide ', app.hideFieldHelper ); // Field map table, update key source $builder.on( 'input', '.wpforms-field-map-table .key-source', function() { const value = $( this ).val(), $dest = $( this ).parent().parent().find( '.key-destination' ), name = $dest.data( 'name' ); if ( value ) { $dest.attr( 'name', name.replace( '{source}', value.replace( /[^0-9a-zA-Z_-]/gi, '' ) ) ); } } ); // Field map table, delete row $builder.on( 'click', '.wpforms-field-map-table .remove', function( e ) { e.preventDefault(); app.fieldMapTableDeleteRow( e, $( this ) ); } ); // Field map table, Add row $builder.on( 'click', '.wpforms-field-map-table .add', function( e ) { e.preventDefault(); app.fieldMapTableAddRow( e, $( this ) ); } ); // Global select field mapping $( document ).on( 'wpformsFieldUpdate', app.fieldMapSelect ); // Restrict user money input fields $builder.on( 'input', '.wpforms-money-input', function( event ) { // eslint-disable-line no-unused-vars const $this = $( this ), amount = $this.val(), start = $this[ 0 ].selectionStart, end = $this[ 0 ].selectionEnd; $this.val( amount.replace( /[^0-9.,]/g, '' ) ); $this[ 0 ].setSelectionRange( start, end ); } ); // Format user money input fields $builder.on( 'focusout', '.wpforms-money-input', function( event ) { // eslint-disable-line no-unused-vars const $this = $( this ), amount = $this.val(); if ( ! amount ) { return amount; } const sanitized = wpf.amountSanitize( amount ), formatted = wpf.amountFormat( sanitized ); $this.val( formatted ); } ); // Show/hide a group of options. $builder.on( 'change', '.wpforms-panel-field-toggle', function() { const $input = $( this ); if ( $input.prop( 'disabled' ) ) { return; } $input.prop( 'disabled', true ); app.toggleOptionsGroup( $input ); } ); // Don't allow users to enable payments if storing entries has // been disabled in the General settings. $builder.on( 'change', app.getPaymentsTogglesSelector(), function( event ) { // eslint-disable-line no-unused-vars const $this = $( this ), gateway = $this.attr( 'id' ).replace( /wpforms-panel-field-|-enable|_one_time|_recurring/gi, '' ), $notificationWrap = $( '.wpforms-panel-content-section-notifications [id*="-' + gateway + '-wrap"]' ), gatewayEnabled = $this.prop( 'checked' ) || $( '#wpforms-panel-field-' + gateway + '-enable_one_time' ).prop( 'checked' ) || $( '#wpforms-panel-field-' + gateway + '-enable_recurring' ).prop( 'checked' ); if ( gatewayEnabled ) { const disabled = $( '#wpforms-panel-field-settings-disable_entries' ).prop( 'checked' ); if ( disabled ) { $.confirm( { title: wpforms_builder.heads_up, content: wpforms_builder.payments_entries_off, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); $this.prop( 'checked', false ); } else { $notificationWrap.removeClass( 'wpforms-hidden' ); } } else { $notificationWrap.addClass( 'wpforms-hidden' ); $notificationWrap.find( 'input[id*="-' + gateway + '"]' ).prop( 'checked', false ); } } ); // Disable entries toggle. $builder.on( 'change', '#wpforms-panel-field-settings-disable_entries', function( event ) { // eslint-disable-line no-unused-vars const $this = $( this ); // Toggle the store spam entries toggle. $( '#wpforms-panel-field-settings-store_spam_entries-wrap' ).toggleClass( 'wpforms-hidden', $this.prop( 'checked' ) ); // Don't allow users to disable entries if payments have been enabled. if ( $this.prop( 'checked' ) ) { if ( app.isPaymentsEnabled() ) { $.confirm( { title: wpforms_builder.heads_up, content: wpforms_builder.payments_on_entries_off, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); $this.prop( 'checked', false ); } else { $.alert( { title: wpforms_builder.heads_up, content: wpforms_builder.disable_entries, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, } ); } } } ); // Upload or add an image. $builder.on( 'click', '.wpforms-image-upload-add', function( event ) { event.preventDefault(); const $this = $( this ); const $container = $this.parent(); const mediaFrame = wpf.initMediaLibrary( { title: wpforms_builder.upload_image_title, extensions: wpforms_builder.upload_image_extensions, extensionsError: wpforms_builder.upload_image_extensions_error, buttonText: wpforms_builder.upload_image_button, } ); mediaFrame.on( 'select', function() { const mediaAttachment = mediaFrame.state().get( 'selection' ).first().toJSON(); const $preview = $container.find( '.preview' ); $container.find( '.source' ).val( mediaAttachment.url ); $preview.empty(); $preview.prepend( '' ); if ( $this.data( 'after-upload' ) === 'hide' ) { $this.hide(); } $builder.trigger( 'wpformsImageUploadAdd', [ $this, $container ] ); } ).on( 'close', function() { mediaFrame.off( 'library:selection:add' ); } ); // Now that everything has been set, let's open up the frame. mediaFrame.open(); } ); // Remove and uploaded image. $builder.on( 'click', '.wpforms-image-upload-remove', function( event ) { event.preventDefault(); const $container = $( this ).parent().parent(); $container.find( '.preview' ).empty(); $container.find( '.wpforms-image-upload-add' ).show(); $container.find( '.source' ).val( '' ); $builder.trigger( 'wpformsImageUploadRemove', [ $( this ), $container ] ); } ); // Validate email smart tags in Notifications fields. $builder.on( 'blur', '.wpforms-notification .wpforms-panel-field-text input', function() { app.validateEmailSmartTags( $( this ) ); } ); $builder.on( 'blur', '.wpforms-notification .wpforms-panel-field-textarea textarea', function() { app.validateEmailSmartTags( $( this ) ); } ); // Validate From Email in Notification settings. $builder.on( 'focusout', '.wpforms-notification .wpforms-panel-field.js-wpforms-from-email-validation input', app.validateFromEmail ); $builder.on( 'wpformsPanelSectionSwitch', app.notificationsPanelSectionSwitch ); // Mobile notice primary button / close icon click. $builder.on( 'click', '#wpforms-builder-mobile-notice .wpforms-fullscreen-notice-button-primary, #wpforms-builder-mobile-notice .close', function() { window.location.href = wpforms_builder.exit_url; } ); // Mobile notice secondary button click. $builder.on( 'click', '#wpforms-builder-mobile-notice .wpforms-fullscreen-notice-button-secondary', function() { window.location.href = wpf.updateQueryString( 'force_desktop_view', 1, window.location.href ); } ); // License Alert close button click. $( '#wpforms-builder-license-alert .close' ).on( 'click', function() { window.location.href = wpforms_builder.exit_url; } ); // License Alert dismiss button click. $( '#wpforms-builder-license-alert .dismiss' ).on( 'click', function( event ) { event.preventDefault(); $( '#wpforms-builder-license-alert' ).remove(); wpCookies.set( 'wpforms-builder-license-alert', 'true', 3600 ); } ); // Don't allow the Akismet setting to be enabled if the Akismet plugin isn't available. $builder.on( 'change', '#wpforms-panel-field-settings-akismet.wpforms-akismet-disabled', function( event ) { // eslint-disable-line no-unused-vars const $this = $( this ), akismetStatus = $this.data( 'akismet-status' ); if ( $this.prop( 'checked' ) ) { $.alert( { title: wpforms_builder.heads_up, content: wpforms_builder[ akismetStatus ], icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, onClose() { $this.prop( 'checked', false ); }, } ); } } ); // Re-init Show More button for multiselect instances when it's visible. $builder.on( 'wpformsPanelSectionSwitch wpformsPanelSwitched', function() { wpf.reInitShowMoreChoices( $( '#wpforms-panel-providers, #wpforms-panel-settings' ) ); } ); }, /** * Notification section switch event handler. * * @since 1.8.2.3 * * @param {Object} e Event object. * @param {string} panel Panel name. */ notificationsPanelSectionSwitch( e, panel ) { if ( panel !== 'notifications' ) { return; } $( '.wpforms-notification .wpforms-panel-field.js-wpforms-from-email-validation input' ).trigger( 'focusout' ); }, /** * Check if one of the payment addons payments enabled. * * @since 1.7.5 * * @return {boolean} True if one of the payment addons payment enabled. */ isPaymentsEnabled() { let paymentEnabled = false; $( app.getPaymentsTogglesSelector() ).each( function() { if ( $( this ).prop( 'checked' ) ) { paymentEnabled = true; return false; } } ); return paymentEnabled; }, /** * Get Payments toggles selector. * * @since 1.7.5 * * @return {string} List of selectors. */ getPaymentsTogglesSelector() { return `.wpforms-panel-content-section-payment-toggle-one-time input, .wpforms-panel-content-section-payment-toggle-recurring input, #wpforms-panel-field-stripe-enable, #wpforms-panel-field-paypal_standard-enable, #wpforms-panel-field-authorize_net-enable, #wpforms-panel-field-square-enable`; }, /** * Toggle an options group. * * @since 1.6.3 * * @param {Object} $input Toggled field. */ toggleOptionsGroup( $input ) { const name = $input.attr( 'name' ); let value = ''; const $body = $( '.wpforms-panel-field-toggle-body[data-toggle="' + name + '"]' ), enableInput = function() { $input.prop( 'disabled', false ); }; app.toggleProviderActiveIcon( $input ); if ( $body.length === 0 ) { enableInput(); return; } const type = $input.attr( 'type' ); if ( 'checkbox' === type || 'radio' === type ) { value = $input.prop( 'checked' ) ? $input.val() : '0'; } else { value = $input.val(); } $body.each( function() { const $this = $( this ); // eslint-disable-next-line no-unused-expressions $this.attr( 'data-toggle-value' ).toString() === value.toString() ? $this.slideDown( '', enableInput ) : $this.slideUp( '', enableInput ); } ); }, /** * Toggle Provider Active icon. * * @since 1.9.3 * * @param {Object} $input Toggled field. */ toggleProviderActiveIcon( $input ) { const provider = $input.closest( '.wpforms-panel-content-section' ).data( 'provider' ); const wrappers = [ 'wpforms-panel-field-' + provider + '-enable-wrap', 'wpforms-panel-field-' + provider + '-enable_one_time-wrap', 'wpforms-panel-field-' + provider + '-enable_recurring-wrap', ]; if ( ! provider || ! wrappers.includes( $input.attr( 'id' ) ) ) { return; } let isActive = false; wrappers.forEach( ( wrapper ) => { const $wrapper = $( '#' + wrapper ); if ( $wrapper.length && $wrapper.find( 'input' ).is( ':checked' ) ) { isActive = true; } } ); const $sidebar = $( `.wpforms-panel-sidebar-section[data-section=${ provider }]` ), $check_icon = $sidebar.find( '.fa-check-circle-o' ); $check_icon.toggleClass( 'wpforms-hidden', ! isActive ); }, /** * Toggle all option groups. * * @since 1.6.3 * * @param {jQuery} $context Context container jQuery object. */ toggleAllOptionGroups( $context ) { $context = $context || $builder || $( '#wpforms-builder' ) || $( 'body' ); if ( ! $context ) { return; } // Show a toggled bodies. $context.find( '.wpforms-panel-field-toggle' ).each( function() { const $input = $( this ); $input.prop( 'disabled', true ); app.toggleOptionsGroup( $input ); } ); }, /** * Toggle unfoldable group of fields. * * @since 1.6.8 * * @param {Object} e Event object. */ toggleUnfoldableGroup( e ) { e.preventDefault(); const $title = $( e.target ), $group = $title.closest( '.wpforms-panel-fields-group' ), $inner = $group.find( '.wpforms-panel-fields-group-inner' ), cookieName = 'wpforms_fields_group_' + $group.data( 'group' ); if ( $group.hasClass( 'opened' ) ) { wpCookies.remove( cookieName ); $inner.stop().slideUp( 150, function() { $group.removeClass( 'opened' ); } ); } else { wpCookies.set( cookieName, 'true', 2592000 ); // 1 month. $group.addClass( 'opened' ); $inner.stop().slideDown( 150 ); } }, /** * Hide field preview helper box. * * @since 1.7.1 * * @param {Object} e Event object. */ hideFieldHelper( e ) { e.preventDefault(); e.stopPropagation(); const $helpers = $( '.wpforms-field-helper' ), cookieName = 'wpforms_field_helper_hide'; wpCookies.set( cookieName, 'true', 30 * 24 * 60 * 60 ); // 1 month. $helpers.hide(); }, /** * Smart Tag toggling. * * @since 1.0.1 * @since 1.6.9 Simplify method. * * @param {Event} e Event. */ smartTagToggle( e ) { e.preventDefault(); // Prevent ajax to validate the default email queued on focusout event. elements.$focusOutTarget = null; const $this = $( this ), $wrapper = $this.closest( '.wpforms-panel-field,.wpforms-field-option-row' ); if ( $wrapper.hasClass( 'smart-tags-toggling' ) ) { return; } $wrapper.addClass( 'smart-tags-toggling' ); if ( $this.hasClass( 'smart-tag-showing' ) ) { app.removeSmartTagsList( $this ); return; } app.insertSmartTagsList( $this ); }, /** * Remove Smart Tag list. * * @since 1.6.9 * * @param {jQuery} $el Toggle element. */ removeSmartTagsList( $el ) { const $wrapper = $el.closest( '.wpforms-panel-field,.wpforms-field-option-row' ), $list = $wrapper.find( '.smart-tags-list-display' ); $el.find( 'span' ).text( wpforms_builder.smart_tags_show ); $list.slideUp( '', function() { $list.remove(); $el.removeClass( 'smart-tag-showing' ); $wrapper.removeClass( 'smart-tags-toggling' ); } ); }, /** * Insert Smart Tag list. * * @since 1.6.9 * * @param {jQuery} $el Toggle element. */ insertSmartTagsList( $el ) { const $wrapper = $el.closest( '.wpforms-panel-field,.wpforms-field-option-row' ); let $label = $el.closest( 'label' ), insideLabel = true; if ( ! $label.length ) { $label = $wrapper.find( 'label' ); insideLabel = false; } const smartTagList = app.getSmartTagsList( $el, $label.attr( 'for' ).indexOf( 'wpforms-field-option-' ) !== -1 ); // eslint-disable-next-line no-unused-expressions insideLabel ? $label.after( smartTagList ) : $el.after( smartTagList ); $el.find( 'span' ).text( wpforms_builder.smart_tags_hide ); $wrapper.find( '.smart-tags-list-display' ).slideDown( '', function() { $el.addClass( 'smart-tag-showing' ); $wrapper.removeClass( 'smart-tags-toggling' ); } ); }, /** * Get Smart Tag list markup. * * @since 1.6.9 * * @param {jQuery} $el Toggle element. * @param {boolean} isFieldOption Is a field option. * * @return {string} Smart Tags list markup. */ getSmartTagsList( $el, isFieldOption ) { let smartTagList; smartTagList = '
      '; smartTagList += app.getSmartTagsListFieldsElements( $el ); smartTagList += app.getSmartTagsListOtherElements( $el, isFieldOption ); smartTagList += '
    '; return smartTagList; }, /** * Get Smart Tag fields elements markup. * * @since 1.6.9 * * @param {jQuery} $el Toggle element. * * @return {string} Smart Tags list elements markup. */ getSmartTagsListFieldsElements( $el ) { const type = $el.data( 'type' ); if ( ! [ 'fields', 'all' ].includes( type ) ) { return ''; } const fields = app.getSmartTagsFields( $el ); if ( ! fields ) { return '
  • ' + wpforms_builder.fields_unavailable + '
  • '; } let smartTagListElements = ''; smartTagListElements += '
  • ' + wpforms_builder.fields_available + '
  • '; for ( const fieldKey in fields ) { smartTagListElements += app.getSmartTagsListFieldsElement( fields[ fieldKey ] ); } return smartTagListElements; }, /** * Get fields that possible to create smart tag. * * @since 1.6.9 * * @param {jQuery} $el Toggle element. * * @return {Array} Fields for smart tags. */ getSmartTagsFields( $el ) { const allowed = $el.data( 'fields' ); const isAllowedRepeater = $el.data( 'allow-repeated-fields' ); const allowedFields = allowed ? allowed.split( ',' ) : undefined; return wpf.getFields( allowedFields, true, isAllowedRepeater ); }, /** * Get field markup for the Smart Tags list. * * @since 1.6.9 * * @param {Object} field A field. * * @return {string} Smart Tags field markup. */ getSmartTagsListFieldsElement( field ) { const label = field.label ? wpf.encodeHTMLEntities( wpf.sanitizeHTML( field.label ) ) : wpforms_builder.field + ' #' + field.id; let html = `
  • ${ label }
  • `; const additionalTags = field.additional || []; // Add additional tags for `name`, `date/time` and `address` fields. if ( additionalTags.length > 1 ) { additionalTags.forEach( ( additionalTag ) => { // Capitalize the first letter and add space before numbers. const additionalTagLabel = additionalTag.charAt( 0 ).toUpperCase() + additionalTag.slice( 1 ).replace( /(\D)(\d)/g, '$1 $2' ); html += `
  • ${ label } – ${ additionalTagLabel }
  • `; } ); } return html; }, /** * Get Smart Tag other elements markup. * * @since 1.6.9 * * @param {jQuery} $el Toggle element. * @param {boolean} isFieldOption Is a field option. * * @return {string} Smart Tags list elements markup. */ getSmartTagsListOtherElements( $el, isFieldOption ) { const type = $el.data( 'type' ); let smartTagListElements; if ( type !== 'other' && type !== 'all' ) { return ''; } smartTagListElements = '
  • ' + wpforms_builder.other + '
  • '; for ( const smartTagKey in wpforms_builder.smart_tags ) { if ( ( isFieldOption && wpforms_builder.smart_tags_disabled_for_fields.includes( smartTagKey ) ) || ( $el.data( 'location' ) === 'confirmations' && wpforms_builder.smart_tags_disabled_for_confirmations.includes( smartTagKey ) ) ) { continue; } smartTagListElements += '
  • ' + wpforms_builder.smart_tags[ smartTagKey ] + '
  • '; } return smartTagListElements; }, /** * Smart Tag insert. * * @since 1.0.1 * @since 1.6.9 TinyMCE compatibility. * * @param {Event} e Event. */ smartTagInsert( e ) { // eslint-disable-line complexity e.preventDefault(); const $this = $( this ), $list = $this.closest( '.smart-tags-list-display' ), $wrapper = $list.closest( '.wpforms-panel-field,.wpforms-field-option-row' ), $toggle = $wrapper.find( '.toggle-smart-tag-display' ), $input = $wrapper.find( 'input[type=text], textarea' ), meta = $this.data( 'meta' ), additional = $this.data( 'additional' ) ? '|' + $this.data( 'additional' ) : '', type = $this.data( 'type' ); let smartTag = type === 'field' ? '{field_id="' + meta + additional + '"}' : '{' + meta + '}', editor; if ( typeof tinyMCE !== 'undefined' ) { editor = tinyMCE.get( $input.prop( 'id' ) ); if ( editor && ! editor.hasFocus() ) { editor.focus( true ); } } if ( editor && ! editor.isHidden() ) { editor.insertContent( smartTag ); } else { smartTag = ' ' + smartTag + ' '; $input.insertAtCaret( smartTag ); // Remove redundant spaces after wrapping smartTag into spaces. $input.val( $input.val().trim().replace( ' ', ' ' ) ); $input.trigger( 'focus' ).trigger( 'input' ); } // Remove the list, all done! $list.slideUp( '', function() { $list.remove(); } ); $toggle.find( 'span' ).text( wpforms_builder.smart_tags_show ); $wrapper.find( '.toggle-smart-tag-display' ).removeClass( 'smart-tag-showing' ); }, /** * Field map table - Delete row. * * @since 1.2.0 * @since 1.6.1.2 Registered `wpformsFieldMapTableDeletedRow` trigger. * * @param {Event} e Event. * @param {Element} el Element. */ fieldMapTableDeleteRow( e, el ) { const $this = $( el ), $row = $this.closest( 'tr' ), $table = $this.closest( 'table' ), $block = $row.closest( '.wpforms-builder-settings-block' ), total = $table.find( 'tr' ).length; if ( total > '1' ) { $row.remove(); $builder.trigger( 'wpformsFieldMapTableDeletedRow', [ $block ] ); } }, /** * Field map table - Add row. * * @since 1.2.0 * @since 1.6.1.2 Registered `wpformsFieldMapTableAddedRow` trigger. * * @param {Event} e Event. * @param {Element} el Element. */ fieldMapTableAddRow( e, el ) { const $this = $( el ), $row = $this.closest( 'tr' ), $block = $row.closest( '.wpforms-builder-settings-block' ), choice = $row.clone().insertAfter( $row ); choice.find( 'input' ).val( '' ); choice.find( 'select :selected' ).prop( 'selected', false ); choice.find( '.key-destination' ).attr( 'name', '' ); $builder.trigger( 'wpformsFieldMapTableAddedRow', [ $block, choice ] ); }, /** * Update field mapped select items on form updates. * * @since 1.2.0 * @since 1.6.1.2 Registered `wpformsFieldSelectMapped` trigger. * * @param {Event} e Event. * @param {Object} fields Fields. */ fieldMapSelect( e, fields ) { // eslint-disable-line max-lines-per-function const event = WPFormsUtils.triggerEvent( $builder, 'wpformsBeforeFieldMapSelectUpdate' ); // Allow callbacks on `wpformsBeforeFieldMapSelectUpdate` to cancel adding field // by triggering `event.preventDefault()`. if ( event.isDefaultPrevented() ) { return; } $( '.wpforms-field-map-select' ).each( function( index, el ) { // eslint-disable-line complexity, no-unused-vars const $this = $( this ); let allowedFields = $this.data( 'field-map-allowed' ), placeholder = $this.data( 'field-map-placeholder' ); // Check if custom placeholder was provided. if ( typeof placeholder === 'undefined' || ! placeholder ) { placeholder = wpforms_builder.select_field; } // If allowed, fields are not defined, bail. if ( typeof allowedFields !== 'undefined' && allowedFields ) { allowedFields = allowedFields.split( ' ' ); } else { return; } const selected = $this.find( 'option:selected' ).val(); // Reset select and add a placeholder option. $this.empty().append( $( '"):t.length&&t.remove()},isModernSelect(e){e=k.dropdownField.helpers.getInstance(e);return"object"==typeof e&&!v.isEmptyObject(e)&&e.initialised},setInstance(e,i){e.data("choicesjs",i)},getInstance(e){return e.data("choicesjs")},getDynamicChoicesOption(e){e=v("#wpforms-field-option-"+e+"-dynamic_choices");return!!e.length&&e},isDynamicChoices(e){e=k.dropdownField.helpers.getDynamicChoicesOption(e);return!!e.length&&""!==e.val()},isDynamicChoicesOptionModern(e){e=v("#wpforms-field-option-"+e+"-style");return!!e.length&&"modern"===e.val()},getDynamicChoicesOptionType(e){e=k.dropdownField.helpers.getDynamicChoicesOption(e);return!!e.length&&e.val()},getDynamicChoicesOptionSource(e){var i=k.dropdownField.helpers.getDynamicChoicesOptionType(e),e=v("#wpforms-field-option-"+e+"-dynamic_"+i);return!!e.length&&e.find("option:selected").text()},hasDefaults(e){return!!v("#wpforms-field-option-row-"+e+"-choices .choices-list").find("input.default:checked").length},getPrimarySelector(e){return v("#wpforms-field-"+e+" .primary-input")}}},numberSliderEvents(e){e.on("focusout",".wpforms-field-option-row-min_max .wpforms-input-row .wpforms-number-slider-min",k.fieldNumberSliderUpdateMin),e.on("focusout",".wpforms-field-option-row-min_max .wpforms-input-row .wpforms-number-slider-max",k.fieldNumberSliderUpdateMax),e.on("input",".wpforms-number-slider-default-value",_.debounce(k.changeNumberSliderDefaultValue,500)),e.on("focusout",".wpforms-number-slider-default-value",k.changeNumberSliderEmptyDefaultValue),e.find(".wpforms-number-slider-default-value").trigger("input"),e.on("input",".wpforms-number-slider-step",_.debounce(k.changeNumberSliderStep,500)),e.on("focusout",".wpforms-number-slider-step",k.checkNumberSliderStep),e.on("input",".wpforms-number-slider-value-display",_.debounce(k.changeNumberSliderValueDisplay,500)),e.on("input",".wpforms-number-slider-min",_.debounce(k.changeNumberSliderMin,500)),e.on("input",".wpforms-number-slider-max",_.debounce(k.changeNumberSliderMax,500))},changeNumberSliderMin(e){var i=parseFloat(e.target.value);isNaN(i)||(i=v(e.target).parents(".wpforms-field-option-row").data("fieldId"),k.updateNumberSliderDefaultValueAttr(i,e.target.value,"min"))},changeNumberSliderMax(e){var i=parseFloat(e.target.value);isNaN(i)||(i=v(e.target).parents(".wpforms-field-option-row").data("fieldId"),k.updateNumberSliderDefaultValueAttr(i,e.target.value,"max").updateNumberSliderStepValueMaxAttr(i,e.target.value))},changeNumberSliderValueDisplay(e){var i=e.target.value,e=v(e.target).parents(".wpforms-field-option-row").data("fieldId"),o=r.getElementById("wpforms-field-option-"+e+"-default_value");o&&k.updateNumberSliderHintStr(e,i).updateNumberSliderHint(e,o.value)},changeNumberSliderStep(e){var i,o,t=v(this),r=parseFloat(t.val());isNaN(r)||r<=0||(i=v(t).closest(".wpforms-field-option"),(o=(parseFloat(i.find(".wpforms-number-slider-max").val())-(i=parseFloat(i.find(".wpforms-number-slider-min").val()))).toFixed(2))e%1==0?e.toString():e.toFixed(2))(i),(r=o(r))===(o=o(t))||r===i||o===i?k.removeNotice(e):(t=wpforms_builder.number_slider_error_valid_default_value.replace("{from}",r).replace("{to}",o),k.printNotice(t,e))),this},printNotice(e,i){i.length&&(this.removeNotice(i),i.append(`

    ${e}

    `))},removeNotice(e){e.length&&e.find(".wpforms-alert").length&&e.find(".wpforms-alert").remove()},checkNumberSliderStep(e){e=parseFloat(e.target.value);if(isNaN(e)||!(0"+i+"")),this},fieldNumberSliderUpdateMin(e){var i,o,t=parseFloat(e.target.value);isNaN(t)||(i=v(e.target).parents(".wpforms-field-option-row-min_max"),(o=parseFloat(i.find(".wpforms-number-slider-max").val()))<=t?(e.preventDefault(),this.value=o):(e=i.data("field-id"),y.find("#wpforms-field-"+e+' input[type="range"]').attr("min",t)))},fieldNumberSliderUpdateMax(e){var i,o,t=parseFloat(e.target.value);isNaN(t)||(i=v(e.target).parents(".wpforms-field-option-row-min_max"),t<=(o=parseFloat(i.find(".wpforms-number-slider-min").val()))?(e.preventDefault(),this.value=o):(e=i.data("field-id"),y.find("#wpforms-field-"+e+' input[type="range"]').attr("max",t)))},updateNumberSliderStepValueMaxAttr(e,i){var o,e=r.getElementById("wpforms-field-option-"+e+"-step");return e&&(o=parseFloat(e.value),e.setAttribute("max",i),(i=parseFloat(i)) .label-title .text").text(r)}),y.on("input",".wpforms-field-option-row-description textarea",function(){var e=v(this),i=wpf.sanitizeHTML(e.val()),o=e.parent().data("field-id"),t=v(`#wpforms-field-${o} > .description, #wpforms-field-${o} .wpforms-field-internal-information-row-description`);k.updateDescription(t,i),e.trigger("wpformsDescriptionFieldUpdated",{id:o,descField:t,value:i})}),y.on("change",".wpforms-field-option-row-required input",function(e){var i=v(this).closest(".wpforms-field-option-row").data("field-id");v("#wpforms-field-"+i).toggleClass("required")}),y.on("change",".wpforms-field-option-row-summary input",function(){var e=v(this),i=e.closest(".wpforms-field-option-row").data("field-id");v("#wpforms-field-"+i).toggleClass("wpforms-summary-enabled"),e.closest(".wpforms-field-option-group-inner").find(".wpforms-total-summary-alert").toggleClass("wpforms-hidden")}),y.on("change",".wpforms-field-option-row-confirmation input",function(){var e=v(this).closest(".wpforms-field-option-row").data("field-id");v("#wpforms-field-"+e).find(".wpforms-confirm").toggleClass("wpforms-confirm-enabled wpforms-confirm-disabled"),v("#wpforms-field-option-"+e).toggleClass("wpforms-confirm-enabled wpforms-confirm-disabled")}),y.on("change",".wpforms-field-option-row-filter_type select",function(){var e=v(this).parent().data("field-id"),e=v("#wpforms-field-option-"+e);v(this).val()?(e.removeClass("wpforms-filter-allowlist"),e.removeClass("wpforms-filter-denylist"),e.addClass("wpforms-filter-"+v(this).val())):(e.removeClass("wpforms-filter-allowlist"),e.removeClass("wpforms-filter-denylist"))}),y.on("focusout",".wpforms-field-option-row-allowlist textarea,.wpforms-field-option-row-denylist textarea",function(){const o=v(this);let e="allow";var i,t;o.next(".wpforms-alert").remove(),""!==o.val()&&(i=v(".wpforms-field-option-row-allowlist textarea"),t=v(".wpforms-field-option-row-denylist textarea"),o.is(t)&&(e="deny"),v.get(wpforms_builder.ajax_url,{nonce:wpforms_builder.nonce,content:JSON.stringify({allow:i.val(),deny:t.val(),current:e}),action:"wpforms_sanitize_restricted_rules"},function(e){var i;e.success&&(o.val(e.data.currentField),0!==(i=e.data.intersect).length&&(i="

    "+wpforms_builder.allow_deny_lists_intersect+'

    '+i+"

    ",v.alert({title:wpforms_builder.heads_up,content:i,icon:"fa fa-exclamation-circle",type:"red",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}})),e.data.restricted||0)&&o.after('

    '+wpforms_builder.restricted_rules+"

    ")}))}),y.on("focusout",C.defaultEmailSelector,function(){C.$focusOutTarget=v(this),k.focusOutEvent()}),y.on("change",".wpforms-field-option-row-size select",function(e){var i=v(this),o=i.val(),i=i.parent().data("field-id");v("#wpforms-field-"+i).removeClass("size-small size-medium size-large").addClass("size-"+o)}),y.on("input",".wpforms-field-option-row-placeholder input",function(){var e=v(this),i=e.parent().data("field-id"),o=v("#wpforms-field-"+i),t=o.find(".primary-input");let r=wpf.sanitizeHTML(e.val());"payment-single"===o.data("field-type")&&""===r&&(r=v("#wpforms-field-option-"+i+"-price").prop("placeholder")),t.is("select")?k.dropdownField.helpers.isModernSelect(t)?(e=k.dropdownField.helpers.getInstance(t),t.prop("multiple")?v(e.input.element).prop("placeholder",r):(e.setChoiceByValue(""),t.closest(".choices").find(".choices__inner .choices__placeholder").text(r),o=v("#wpforms-field-option-"+i+"-dynamic_choices").val(),k.dropdownField.helpers.update(i,o))):(e=t.find(".placeholder"),!r.length&&e.length?e.remove():(e.length?e.text(r):t.prepend('"),t.find(".placeholder").prop("selected",!t.prop("multiple")))):t.prop("placeholder",r)}),y.on("input",".wpforms-field-option-row-confirmation_placeholder input",function(e){var i=v(this),o=i.val(),i=i.parent().data("field-id");v("#wpforms-field-"+i).find(".secondary-input").attr("placeholder",o)}),y.on("input",".wpforms-field-option .format-selected input.placeholder",function(){var e=v(this),i=e.val(),e=e.closest(".wpforms-field-option-row"),o=e.data("field-id"),e=e.data("subfield");v("#wpforms-field-"+o).find(".wpforms-"+e+" input").attr("placeholder",i)}),y.on("input",".wpforms-field-option-address input.placeholder",function(){var e=v(this),i=e.closest(".wpforms-field-option-row"),o=i.data("field-id"),t=i.data("subfield"),r=v("#wpforms-field-"+o+" .wpforms-"+t).find("input, select"),i=i.find("#wpforms-field-option-"+o+"-"+t+"_default");const s=i.val(),n=i.find("option:selected").text(),l=e.val();r.each(function(){var e,i,o=v(this);o.is("select")?(e=o.find(".placeholder"),i=""===s&&""!==l?l:n,e.text(i)):o.attr("placeholder",l)})}),y.on("input",".wpforms-field-option-row-default_value input",function(){var e=v(this),i=wpf.sanitizeHTML(e.val()),e=e.closest(".wpforms-field-option-row").data("field-id");v("#wpforms-field-"+e+" .primary-input").val(i)}),y.on("input",".wpforms-field-options-column input.default",function(){var e=v(this),i=wpf.sanitizeHTML(e.val()),e=e.closest(".wpforms-field-option-row"),o=e.data("field-id"),e=e.data("subfield");v("#wpforms-field-"+o+" .wpforms-"+e+" input").val(i)}),y.on("change",".wpforms-field-option-address select.default",function(){var e=v(this),i=e.val(),o=e.find("option:selected").text(),e=e.closest(".wpforms-field-option-row"),t=e.data("field-id"),r=e.data("subfield"),s=v("#wpforms-field-option-"+t+"-scheme").val(),e=e.find("#wpforms-field-option-"+t+"-"+r+"_placeholder").val(),t=v("#wpforms-field-"+t+" .wpforms-address-scheme-"+s+" .wpforms-"+r+" .placeholder");""===i&&0parseInt(i.val(),10))}),y.on("change",".wpforms-field-option-row-format select",function(){var e,i,o,t,r,s=v(this),n=s.val(),s=s.parent().data("field-id"),l=v("#wpforms-field-option-row-"+s+"-sublabel_hide"),a=v("#wpforms-field-"+s);a.find(".format-selected").removeClass().addClass("format-selected format-selected-"+n),v("#wpforms-field-option-"+s).find(".format-selected").removeClass().addClass("format-selected format-selected-"+n),["date-time","first-last","first-middle-last"].includes(n)?l.removeClass("wpforms-hidden"):l.addClass("wpforms-hidden"),v(`#wpforms-field-option-row-${s}-price_label`).toggleClass("wpforms-hidden","single"!==n),["single","user","hidden"].includes(n)&&(l="user"===n,e="single"===n,n="hidden"===n,i=v("#wpforms-field-option-"+s+"-enable_quantity").is(":checked"),t=v("#wpforms-field-option-"+s+"-min_price"),t=(o=wpf.amountSanitize(t.val()))>=t.data("minimum-price"),r=v("#wpforms-field-option-row-"+s+"-min_price"),v("#wpforms-field-option-row-"+s+"-placeholder").toggleClass("wpforms-hidden",!l),v("#wpforms-field-option-row-"+s+"-enable_quantity").toggleClass("wpforms-hidden",!e),v("#wpforms-field-option-row-"+s+"-quantities_alert").toggleClass("wpforms-hidden",!e),v("#wpforms-field-option-row-"+s+"-quantity").toggleClass("wpforms-hidden",!e||!i),a.find(".quantity-input").toggleClass("wpforms-hidden",!e||!i),r.toggleClass("wpforms-hidden",!l),r.find(".wpforms-item-minimum-price-alert").toggleClass("wpforms-hidden",t),a.find(".item-min-price").toggleClass("wpforms-hidden",l&&o<=0),a.toggleClass("min-price-warning",!t),a.find(".fa-exclamation-triangle").toggleClass("wpforms-hidden",t),v(`#wpforms-field-${s} .item-price-single`).toggleClass("wpforms-hidden",!e),v(`#wpforms-field-${s} .item-price-hidden`).toggleClass("wpforms-hidden",!n))}),y.on("change",".wpforms-field-option-row-scheme select",function(e){var i=v(this),o=i.val(),i=i.parent().data("field-id"),t=v("#wpforms-field-"+i),r=v(`#wpforms-field-option-row-${i}-state`),i=v(`#wpforms-field-option-row-${i}-country`),t=(t.find(".wpforms-address-scheme").addClass("wpforms-hide"),t.find(".wpforms-address-scheme-"+o).removeClass("wpforms-hide"),t.find(`.wpforms-address-scheme-${o} .wpforms-country select, .wpforms-address-scheme-${o} .wpforms-country input`)),t=(0===t.length?i.addClass("wpforms-hidden"):i.removeClass("wpforms-hidden"),r.find(".default .default").not(".wpforms-hidden-strict")),r=r.find(`.default [data-scheme="${o}"]`),s=i.find(".default .default").not(".wpforms-hidden-strict"),i=i.find(`.default [data-scheme="${o}"]`);r.attr({id:t.attr("id"),name:t.attr("name")}).removeClass("wpforms-hidden-strict"),t.attr({id:"",name:""}).addClass("wpforms-hidden-strict"),i.attr({id:s.attr("id"),name:s.attr("name")}).removeClass("wpforms-hidden-strict"),s.attr({id:"",name:""}).addClass("wpforms-hidden-strict")}),y.on("change",".wpforms-field-option-row-date .type select",function(e){var i=v(this),o=i.val(),t=v(this).closest(".wpforms-field-option-row").data("field-id"),r="datepicker"===o?"wpforms-date-type-datepicker":"wpforms-date-type-dropdown",s="datepicker"===o?"wpforms-date-type-dropdown":"wpforms-date-type-datepicker",r=(v("#wpforms-field-"+t).find(".wpforms-date").addClass(r).removeClass(s),v("#wpforms-field-option-"+t).addClass(r).removeClass(s),i.closest(".wpforms-field-option-group-advanced").find(".wpforms-field-option-row-date_limit_days, .wpforms-field-option-row-date_limit_days_options, .wpforms-field-option-row-date_disable_past_dates")),s=v("#wpforms-field-option-row-"+t+"-date_limit_days_options");"dropdown"===o?((i=v("#wpforms-field-option-"+t+"-date_format")).find("option:selected").hasClass("datepicker-only")&&i.prop("selectedIndex",0).trigger("change"),r.hide()):(r.show(),v("#wpforms-field-option-"+t+"-date_limit_days").is(":checked")?s.show():s.hide())}),y.on("change",".wpforms-field-option-row-date .format select",function(e){var i=v(this).val(),o=v(this).closest(".wpforms-field-option-row").data("field-id"),o=v("#wpforms-field-"+o);"m/d/Y"===i?(o.find(".wpforms-date-dropdown .first option").text(wpforms_builder.date_select_month),o.find(".wpforms-date-dropdown .second option").text(wpforms_builder.date_select_day)):"d/m/Y"===i&&(o.find(".wpforms-date-dropdown .first option").text(wpforms_builder.date_select_day),o.find(".wpforms-date-dropdown .second option").text(wpforms_builder.date_select_month))}),y.on("change",".wpforms-field-option-row-time .format select",function(e){const i=v(this),r=i.closest(".wpforms-field-option-row").data("field-id");let s="",o;const n=i.val().match(/[gh]/)?12:24,t=12==n?1:0,l=12==n?13:24;for(let e=t;e{hh}'.replace(/{hh}/g,o);_.forEach(["start","end"],function(e){var i=y.find("#wpforms-field-option-"+r+"-time_limit_hours_"+e+"_hour"),e=y.find("#wpforms-field-option-"+r+"-time_limit_hours_"+e+"_ampm");let o=parseInt(i.val(),10),t=e.val();o=(o=24==n?"pm"===t?o+12:o:(t=12=e.data("minimum-price"),e=e.parent(),r=v("#wpforms-field-"+e.data("field-id"));e.find(".wpforms-item-minimum-price-alert").toggleClass("wpforms-hidden",o),r.find(".item-min-price").toggleClass("wpforms-hidden",t),r.toggleClass("min-price-warning",!o),r.find(".fa-exclamation-triangle").toggleClass("wpforms-hidden",o),t||r.find(".min-price").text(wpf.amountFormatCurrency(i))}),y.on("input",".wpforms-single-item-price-label-display",function(){var e=v(this),i=wpf.sanitizeHTML(e.val(),"<>"),o=e.parent().data("field-id"),t=v("#wpforms-field-"+o),o=wpf.amountFormatCurrency(v(`#wpforms-field-option-${o}-price`).val());i?t.find(".price-label").html(i.replaceAll("{price}",` ${o} `)):(e.val("{price}"),t.find(".price-label").html(` ${o} `))}),y.on("change",".wpforms-field-option-credit-card .payment-icons input",function(){var e=v(this),i=e.data("card"),e=e.parent().data("field-id");v("#wpforms-field-"+e).find("img.icon-"+i).toggleClass("card_hide")}),y.on("input",".wpforms-field-option input.placeholder-update",function(e){var i=v(this),o=i.val(),t=i.data("field-id"),i=i.data("subfield");v("#wpforms-field-"+t).find(".wpforms-"+i+" input").attr("placeholder",o)}),y.on("change",".wpforms-field-option-row-input_columns select",function(){var e=v(this),i=e.val(),e=e.parent().data("field-id");let o="";"2"===i?o="wpforms-list-2-columns":"3"===i?o="wpforms-list-3-columns":"inline"===i&&(o="wpforms-list-inline"),v("#wpforms-field-"+e).removeClass("wpforms-list-2-columns wpforms-list-3-columns wpforms-list-inline").addClass(o)}),y.on("change",".wpforms-field-option-row .wpforms-toggle-control input",function(e){var i=v(this),o=i.closest(".wpforms-toggle-control").find(".wpforms-toggle-control-status"),i=i.is(":checked")?"on":"off";o.html(o.data(i))}),y.on("change",".wpforms-field-option-row-dynamic_choices select",function(e){k.fieldDynamicChoiceToggle(v(this))}),y.on("change",".wpforms-field-option-row-dynamic_taxonomy select, .wpforms-field-option-row-dynamic_post_type select",function(e){k.fieldDynamicChoiceSource(v(this))}),y.on("click",".toggle-layout-selector-display",function(e){e.preventDefault(),k.fieldLayoutSelectorToggle(this)}),y.on("click",".layout-selector-display-layout",function(e){e.preventDefault(),k.fieldLayoutSelectorLayout(this)}),y.on("click",".layout-selector-display-columns span",function(e){e.preventDefault(),k.fieldLayoutSelectorInsert(this)}),v(r).on("change",".wpforms-field-option-row-scale select",function(){const e=v(this),i=e.val(),o=e.parent().data("field-id"),t=v("#wpforms-field-"+o+" .rating-icon");let r=1;t.each(function(e){r<=i?v(this).show():v(this).hide(),r++})}),v(r).on("change",".wpforms-field-option-row-icon select",function(){var e=v(this),i=e.val(),e=e.parent().data("field-id"),e=v("#wpforms-field-"+e+" .rating-icon");let o="fa-star";"heart"===i?o="fa-heart":"thumb"===i?o="fa-thumbs-up":"smiley"===i&&(o="fa-smile-o"),e.removeClass("fa-star fa-heart fa-thumbs-up fa-smile-o").addClass(o)}),v(r).on("change",".wpforms-field-option-row-icon_size select",function(){var e=v(this),i=e.val(),e=e.parent().data("field-id"),e=v("#wpforms-field-"+e+" .rating-icon");let o="28";"small"===i?o="18":"large"===i&&(o="38"),e.css("font-size",o+"px")}),v(r).on("input",".wpforms-field-option-row-icon_color input.wpforms-color-picker",function(){var e=v(this),i=e.closest(".wpforms-field-option-row").data("field-id");v("#wpforms-field-"+i+" > i.fa").css("color",k.getValidColorPickerValue(e))}),v(r).on("change",".wpforms-field-option-row-disclaimer_format input",function(){var e=v(this).closest(".wpforms-field-option-row").data("field-id");v("#wpforms-field-"+e+" .description").toggleClass("disclaimer")}),y.on("change",".wpforms-field-option-row-limit_enabled input",function(e){k.updateTextFieldsLimitControls(v(e.target).closest(".wpforms-field-option-row-limit_enabled").data().fieldId,e.target.checked)}),y.on("change",".wpforms-field-option-row-date_disable_past_dates input",function(e){k.updateDisableTodaysDateControls(v(e.target).closest(".wpforms-field-option-row-date_disable_past_dates").data().fieldId,e.target?.checked)}),y.on("change",".wpforms-field-option-row-password-strength input",function(e){k.updatePasswordStrengthControls(v(e.target).parents(".wpforms-field-option-row-password-strength").data().fieldId,e.target.checked)}),y.on("change",".wpforms-field-option-richtext .wpforms-field-option-row-media_enabled input",k.updateRichTextMediaFieldsLimitControls),y.on("change",".wpforms-field-option-richtext .wpforms-field-option-row-style select",k.updateRichTextStylePreview),y.on("change",".wpforms-field-option-file-upload .wpforms-field-option-row-style select, .wpforms-field-option-file-upload .wpforms-field-option-row-max_file_number input",function(e){k.fieldFileUploadPreviewUpdate(e.target)}),k.numberSliderEvents(y),k.fieldDynamicChoiceToggleImageChoices(),k.fieldDynamicChoiceToggleIconChoices(),y.on("change",".wpforms-field-option-row-show_price_after_labels input",function(e){var i=v(this).closest(".wpforms-field-option-group-basic").find(".wpforms-field-option-row-choices .choices-list");k.fieldChoiceUpdate(i.data("field-type"),i.data("field-id"))}),y.on("input",".wpforms-field-option-row-preview-notice textarea",k.updatePreviewNotice).on("change",".wpforms-field-option-row-preview-notice-enable input",k.toggleEntryPreviewNotice).on("wpformsFieldAdd",k.maybeLockEntryPreviewGroupOnAdd).on("wpformsFieldMove",k.maybeLockEntryPreviewGroupOnMove).on("click",".wpforms-entry-preview-block",k.entryPreviewBlockField),k.defaultStateEntryPreviewNotice()},focusOutEvent(){if(null!==C.$focusOutTarget){if(C.$defaultEmail.is(C.$focusOutTarget)){const i=C.$focusOutTarget;if(i.next(".wpforms-alert").remove(),""===i.val())return;v.get(wpforms_builder.ajax_url,{nonce:wpforms_builder.nonce,content:i.val(),action:"wpforms_sanitize_default_email"},function(e){e.success&&(i.val(e.data),i.trigger("input"),e.data||i.after('

    '+wpforms_builder.restricted_default_email+"

    "))})}C.$focusOutTarget=null}},isFieldPreviewActionsDisabled(e){return k.isFormPreviewActionsDisabled(e)||v(e).closest(".wpforms-field").hasClass("ui-sortable-disabled")},isFormPreviewActionsDisabled(e){return v(e).closest(".wpforms-field-wrap").hasClass("ui-sortable-disabled")},fieldGroupToggle(e,i){e=v(e);let o=e.next(".wpforms-add-fields-buttons");const t=o.parent();let r=e.find("i"),s=e.data("group"),n="wpforms_field_group_"+s;"click"===i?(t.hasClass("wpforms-closed")?wpCookies.remove(n):wpCookies.set(n,"true",2592e3),r.toggleClass("wpforms-angle-right"),o.stop().slideToggle("",function(){t.toggleClass("wpforms-closed")})):"load"===i&&(o=e.find(".wpforms-add-fields-buttons"),r=e.find(".wpforms-add-fields-heading i"),s=e.find(".wpforms-add-fields-heading").data("group"),"true"===wpCookies.get("wpforms_field_group_"+s))&&(r.toggleClass("wpforms-angle-right"),o.hide(),e.toggleClass("wpforms-closed"))},updateDescription(e,i){e.hasClass("nl2br")&&(i=i.replace(/\n/g,"
    ")),e.html(i)},defaultStateEntryPreviewNotice(){v(".wpforms-field-option-row-preview-notice-enable input").each(function(){v(this).trigger("change")})},updatePreviewNotice(){var e=v(this),i=wpf.sanitizeHTML(e.val()).trim(),e=e.parent().data("field-id"),e=v("#wpforms-field-"+e).find(".wpforms-entry-preview-notice"),i=i||wpforms_builder.entry_preview_default_notice;k.updateDescription(e,i)},toggleEntryPreviewNotice(){var e=v(this),i=e.closest(".wpforms-field-option").data("field-id"),o=v("#wpforms-field-"+i),i=v("#wpforms-field-option-"+i+" .wpforms-field-option-row-preview-notice"),t=o.find(".wpforms-entry-preview-notice"),o=o.find(".wpforms-alert-info");(e.is(":checked")?(o.hide(),t.show(),i):(i.hide(),t.hide(),o)).show()},fieldDelete(e){var i=v("#wpforms-field-"+e),o=i.data("field-type");"pagebreak"===o&&i.hasClass("wpforms-field-entry-preview-not-deleted")?k.youCantRemovePageBreakFieldPopup():i.hasClass("no-delete")?k.youCantRemoveFieldPopup():k.confirmFieldDeletion(e,o)},youCantRemovePageBreakFieldPopup(){v.alert({title:wpforms_builder.heads_up,content:wpforms_builder.entry_preview_require_page_break,icon:"fa fa-exclamation-circle",type:"red",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}})},youCantReorderFieldPopup(){console.warn('WARNING! Function "WPFormsBuilder.youCantReorderFieldPopup()" has been deprecated, please use the new "WPForms.Admin.Builder.DragFields.youCantReorderFieldPopup()" function instead!'),WPForms.Admin.Builder.DragFields.youCantReorderFieldPopup()},youCantRemoveFieldPopup(){v.alert({title:wpforms_builder.field_locked,content:wpforms_builder.field_locked_no_delete_msg,icon:"fa fa-info-circle",type:"blue",buttons:{confirm:{text:wpforms_builder.close,btnClass:"btn-confirm",keys:["enter"]}}})},validationErrorNotificationPopup(e){v.alert({title:wpforms_builder.heads_up,content:e,icon:"fa fa-exclamation-circle",type:"red",buttons:{confirm:{text:wpforms_builder.close,btnClass:"btn-confirm",keys:["enter"]}}})},confirmFieldDeletion(e,i){var o={id:e,message:wpforms_builder.delete_confirm};WPFormsUtils.triggerEvent(y,"wpformsBeforeFieldDeleteAlert",[o,i]).isDefaultPrevented()||v.confirm({title:!1,content:o.message,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"],action(){k.fieldDeleteById(e)}},cancel:{text:wpforms_builder.cancel}}})},fieldDeleteById(t,r="",e=400){v("#wpforms-field-"+t).fadeOut(e,function(){var e=v(this),i=e.parents(".wpforms-field-layout-columns"),e=(r=e.data("field-type"),y.trigger("wpformsBeforeFieldDelete",[t,r]),e.remove(),v("#wpforms-field-option-"+t).remove(),v(".wpforms-field, .wpforms-title-desc").removeClass("active"),k.fieldTabToggle("add-fields"),v(".wpforms-field-option")),o=y.find(".wpforms-field-submit");e.length<1&&(C.$sortableFieldsWrap.append(C.$noFieldsPreview.clone()),C.$fieldOptions.append(C.$noFieldsOptions.clone()),o.hide()),e.filter(":not(.wpforms-field-option-layout)").length||o.hide(),y.trigger("wpformsFieldDelete",[t,r,i])})},determineActiveSections(){const s=wpf.getQueryString("section");v(".wpforms-panel").each(function(e,i){var o,t=v(this),r=(o=t,((r=s)&&o.hasClass("active")&&(o=o.find(`.wpforms-panel-sidebar-section[data-section="${r}"]`)).length?o:null)||(e=>{e=e.find(".wpforms-panel-sidebar-section.configured").first();return e.length?e:null})(t)||t.find(".wpforms-panel-sidebar-section:first-of-type"));o=t,(t=r)&&(r=t.data("section"),t.addClass("active"),(t=o.find(".wpforms-panel-content-section-"+r)).length?(t.show().addClass("active"),o.find(".wpforms-panel-content-section-default").toggle("default"===r)):o.find(".wpforms-panel-content-section-default").show().addClass("active"),WPFormsUtils.triggerEvent(y,"wpformsPanelSectionSwitch",r))})},loadEntryPreviewFields(){var e=v("#wpforms-panel-fields .wpforms-field-wrap .wpforms-field-entry-preview");e.length&&e.each(function(){k.lockEntryPreviewFieldsPosition(v(this).data("field-id"))})},fieldEntryPreviewDelete(e,i,o){"entry-preview"===o&&(i=(o=v("#wpforms-field-"+i)).prevAll(".wpforms-field-pagebreak").first(),o=o.nextAll(".wpforms-field-pagebreak").first().data("field-id"),o=v("#wpforms-field-option-"+o),i.removeClass("wpforms-field-not-draggable wpforms-field-entry-preview-not-deleted"),o.find(".wpforms-entry-preview-block").removeClass("wpforms-entry-preview-block"),y.trigger("wpformsFieldDragToggle",[i.data("field-id"),i.data("field-type")]))},maybeLockEntryPreviewGroupOnMove(e,i){i.item.hasClass("wpforms-field-pagebreak")&&k.maybeLockEntryPreviewGroupOnAdd(e,i.item.data("field-id"),"pagebreak")},maybeLockEntryPreviewGroupOnAdd(e,i,o){var t,r,s,n,l;"pagebreak"===o&&(r=(t=v("#wpforms-field-"+i)).prevAll(".wpforms-field-entry-preview,.wpforms-field-pagebreak").first(),l=t.nextAll(".wpforms-field-entry-preview,.wpforms-field-pagebreak").first(),r.hasClass("wpforms-field-entry-preview")||l.hasClass("wpforms-field-entry-preview"))&&(s=(n=v("#wpforms-field-option-"+i+" .wpforms-field-option-row-prev_toggle")).find("input"),l=v("#wpforms-field-option-"+l.data("field-id")+" .wpforms-field-option-row-prev_toggle"),r.hasClass("wpforms-field-entry-preview")?(s.attr("checked","checked").trigger("change"),n.addClass("wpforms-entry-preview-block"),l.removeClass("wpforms-entry-preview-block")):(s=r.data("field-id"),l=(n=v("#wpforms-field-option-"+s+" .wpforms-field-option-row-prev_toggle")).find("input"),t.addClass("wpforms-field-not-draggable wpforms-field-entry-preview-not-deleted"),y.trigger("wpformsFieldDragToggle",[i,o]),r.removeClass("wpforms-field-not-draggable wpforms-field-entry-preview-not-deleted"),y.trigger("wpformsFieldDragToggle",[s,r.data("field-type")]),r.prevAll(".wpforms-field-entry-preview,.wpforms-field-pagebreak").first().hasClass("wpforms-field-entry-preview")&&(l.attr("checked","checked").trigger("change"),n.addClass("wpforms-entry-preview-block"))))},entryPreviewBlockField(e){e.preventDefault(),v.alert({title:wpforms_builder.heads_up,content:wpforms_builder.entry_preview_require_previous_button,icon:"fa fa-exclamation-circle",type:"red",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}})},isUncheckedEntryPreviewField(e,i){return!("entry-preview"!==e||i&&i.passed)},addEntryPreviewField(e,i){var o,t,r,s=v("#wpforms-add-fields-entry-preview");s.hasClass("wpforms-entry-preview-adding")||(r=v("#wpforms-panel-fields .wpforms-field-wrap > .wpforms-field"),o=i?.position?i.position:r.length,t=k.isEntryPreviewFieldRequiresPageBreakBefore(r,o),r=k.isEntryPreviewFieldRequiresPageBreakAfter(r,o),s.addClass("wpforms-entry-preview-adding"),(i=i||{}).passed=!0,t||r?t?k.addPageBreakAndEntryPreviewFields(i,o):k.addEntryPreviewAndPageBreakFields(i,o):k.fieldAdd("entry-preview",i).done(function(e){k.lockEntryPreviewFieldsPosition(e.data.field.id)}))},addEntryPreviewFieldAfterPageBreak(e){const i=setInterval(function(){2===v("#wpforms-panel-fields .wpforms-field-wrap").find(".wpforms-pagebreak-bottom, .wpforms-pagebreak-top").length&&(k.fieldAdd("entry-preview",e).done(function(e){k.lockEntryPreviewFieldsPosition(e.data.field.id)}),clearInterval(i))},100)},addPageBreakAndEntryPreviewFields(i,o){const t=3<=v("#wpforms-panel-fields .wpforms-field-wrap > .wpforms-field-pagebreak").length;k.fieldAdd("pagebreak",{position:o}).done(function(e){i.position=t?o+1:o+2,k.addEntryPreviewFieldAfterPageBreak(i);e=v("#wpforms-field-option-"+e.data.field.id).find(".wpforms-field-option-row-prev_toggle");e.find("input").attr("checked","checked").trigger("change"),e.addClass("wpforms-entry-preview-block")})},fieldDuplicate(o){const t=v("#wpforms-field-"+o);t.hasClass("no-duplicate")?v.alert({title:wpforms_builder.field_locked,content:wpforms_builder.field_locked_no_duplicate_msg,icon:"fa fa-info-circle",type:"blue",buttons:{confirm:{text:wpforms_builder.close,btnClass:"btn-confirm",keys:["enter"]}}}):v.confirm({title:!1,content:wpforms_builder.duplicate_confirm,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"],action(){var e,i;this.$$confirm.prop("disabled",!0),WPFormsUtils.triggerEvent(y,"wpformsBeforeFieldDuplicate",[o,t]).isDefaultPrevented()||(e=k.fieldDuplicateRoutine(o,!0),i=v("#wpforms-field-"+e),k.increaseNextFieldIdAjaxRequest(),WPFormsUtils.triggerEvent(y,"wpformsFieldDuplicated",[o,t,e,i]))}},cancel:{text:wpforms_builder.cancel}}})},increaseNextFieldIdAjaxRequest(){v.post(wpforms_builder.ajax_url,{form_id:m.formID,field_id:C.$nextFieldId.val(),nonce:wpforms_builder.nonce,action:"wpforms_builder_increase_next_field_id"})},fieldDuplicateRoutine(e,i=!0){var o=v("#wpforms-field-"+e),t=v("#wpforms-field-option-"+e),r=C.$sortableFieldsWrap.find(">.active"),s=C.$fieldOptions.find(">:visible"),n=s.find(">.active"),l=o.data("field-type"),a=t.attr("class"),d=k.dropdownField.helpers.isModernSelect(o.find("> .choices .primary-input"));wpf.restoreTooltips(t),d&&k.dropdownField.helpers.convertModernToClassic(e);let p=t.html();const f=o.clone(),c=parseInt(C.$nextFieldId.val(),10),m=v(`#wpforms-field-option-${e}-label`),w=(m.length?m:v(`#wpforms-field-option-${e}-name`)).val(),u=c+1,h={};var g=""!==w?w+" "+wpforms_builder.duplicate_copy:wpforms_builder.field+` #${e} `+wpforms_builder.duplicate_copy;h.fieldOptionsID=new RegExp("ID #"+e,"g"),h.fieldID=new RegExp("fields\\["+e+"\\]","g"),h.dataFieldID=new RegExp('data-field-id="'+e+'"',"g"),h.referenceID=new RegExp('data-reference="'+e+'"',"g"),h.elementID=new RegExp('\\b(id|for)="wpforms-(.*?)'+e+'(.*?)"',"ig"),o.after(f),r.removeClass("active"),f.addClass("active").attr({id:"wpforms-field-"+c,"data-field-id":c}),h.elementIdReplace=function(e,i,o,t,r,s){return`${i}="wpforms-${o}${c}${t}"`},p=(p=(p=(p=(p=p.replace(h.fieldOptionsID,"ID #"+c)).replace(h.fieldID,`fields[${c}]`)).replace(h.dataFieldID,`data-field-id="${c}"`)).replace(h.referenceID,`data-reference="${c}"`)).replace(h.elementID,h.elementIdReplace),s.hide(),t.after(`
    ${p}
    `);const b=v("#wpforms-field-option-"+c);r.data("field-id")===e&&n.length&&(o=n.attr("class").match(/wpforms-field-option-group-\S*/i)[0],s=b.find(">."+o),b.find(">").removeClass("active"),s.addClass("active")),r.data("field-id")!==e&&n.length&&(b.find(">").removeClass("active"),b.find(">.wpforms-field-option-group-basic").addClass("active")),t.find(":input").each(function(e,i){var o=v(this),t=o.attr("name");if(!t)return"continue";var t=t.replace(h.fieldID,`fields[${c}]`),r=o.attr("type");"checkbox"===r||"radio"===r?o.is(":checked")?b.find(`[name="${t}"]`).prop("checked",!0).attr("checked","checked"):b.find(`[name="${t}"]`).prop("checked",!1).attr("checked",!1):o.is("select")?o.find("option:selected").length&&(r=o.find("option:selected").val(),b.find(`[name="${t}"]`).find(`[value="${r}"]`).prop("selected",!0)):""===(r=o.val())&&o.hasClass("wpforms-money-input")?b.find(`[name="${t}"]`).val(wpf.numberFormat("0",wpforms_builder.currency_decimals,wpforms_builder.currency_decimal,wpforms_builder.currency_thousands)):b.find(`[name="${t}"]`).val(r)}),b.find(".wpforms-field-option-hidden-id").val(c),C.$nextFieldId.val(u);a=v("html"===l?`#wpforms-field-option-${c}-name`:`#wpforms-field-option-${c}-label`);return i&&a.val(g).trigger("input"),y.trigger("wpformsFieldAdd",[c,l]),wpf.initTooltips(),d&&(k.dropdownField.helpers.convertClassicToModern(e),k.dropdownField.helpers.convertClassicToModern(c)),k.fieldChoiceUpdate(f.data("field-type"),c),k.loadColorPickers(),c},addEntryPreviewAndPageBreakFields(e,o){k.fieldAdd("entry-preview",e).done(function(e){const i=e.data.field.id;k.fieldAdd("pagebreak",{position:o+1}).done(function(e){k.lockEntryPreviewFieldsPosition(i);e=v("#wpforms-field-"+e.data.field.id).nextAll(".wpforms-field-pagebreak, .wpforms-field-entry-preview").first();e.hasClass("wpforms-field-entry-preview")&&k.lockEntryPreviewFieldsPosition(e.data("field-id"))})})},lockEntryPreviewFieldsPosition(e){var i=v("#wpforms-field-"+e),o=i.prevAll(".wpforms-field-pagebreak:not(.wpforms-pagebreak-bottom)").first(),t=i.nextAll(".wpforms-field-pagebreak").first().data("field-id"),t=v("#wpforms-field-option-"+t).find(".wpforms-field-option-row-prev_toggle"),r=t.find("input");i.addClass("wpforms-field-not-draggable"),o.addClass("wpforms-field-not-draggable wpforms-field-entry-preview-not-deleted"),r.prop("checked","checked").trigger("change"),t.addClass("wpforms-entry-preview-block"),v("#wpforms-add-fields-entry-preview").removeClass("wpforms-entry-preview-adding"),y.trigger("wpformsFieldDragToggle",[e,i.data("field-type")]),y.trigger("wpformsFieldDragToggle",[o.data("field-id"),o.data("field-type")])},isEntryPreviewFieldRequiresPageBreakBefore(e,i){e=e.slice(0,i).filter(".wpforms-field-pagebreak,.wpforms-field-entry-preview");let o=!0;return e.length&&v(e.get().reverse()).each(function(){var e=v(this);return!e.hasClass("wpforms-field-entry-preview")&&(e.hasClass("wpforms-field-pagebreak")&&!e.hasClass("wpforms-field-stick")?o=!1:void 0)}),o},isEntryPreviewFieldRequiresPageBreakAfter(e,i){e=e.slice(i).filter(".wpforms-field-pagebreak,.wpforms-field-entry-preview");let o=Boolean(e.length);return e.length&&e.each(function(){var e=v(this);return!e.hasClass("wpforms-field-entry-preview")&&(e.hasClass("wpforms-field-pagebreak")?o=!1:void 0)}),o},fieldAdd(s,n){var e=v("#wpforms-add-fields-"+s);if(!(e.hasClass("upgrade-modal")||e.hasClass("education-modal")||e.hasClass("warning-modal")))if(["captcha_turnstile","captcha_hcaptcha","captcha_recaptcha","captcha_none"].includes(s))k.captchaUpdate();else{if(a=!0,WPForms.Admin.Builder.DragFields.disableDragAndDrop(),k.disableFormActions(),!k.isUncheckedEntryPreviewField(s,n))return n=v.extend({},{position:"bottom",$sortable:"base",placeholder:!1,scroll:!0,defaults:!1},n),e={action:"wpforms_new_field_"+s,id:m.formID,type:s,defaults:n.defaults,nonce:wpforms_builder.nonce},v.post(wpforms_builder.ajax_url,e,function(i){if(i.success){var o=C.$sortableFieldsWrap,t=v(i.data.preview),r=v(i.data.options);let e=n.$sortable;a=!1,t.css("display","none"),n.placeholder&&n.placeholder.remove(),"default"!==n.$sortable&&n.$sortable.length||(e=o.find(".wpforms-fields-sortable-default")),"base"!==n.$sortable&&e.length||(e=o);o=WPFormsUtils.triggerEvent(y,"wpformsBeforeFieldAddToDOM",[n,t,r,e]);o.isDefaultPrevented()||(o.skipAddFieldToBaseLevel||k.fieldAddToBaseLevel(n,t,r),t.fadeIn(),y.find(".no-fields, .no-fields-preview").remove(),v(".wpforms-field-option:not(.wpforms-field-option-layout)").length&&y.find(".wpforms-field-submit").show(),n.scroll&&n.position.length&&k.scrollPreviewToField(i.data.field.id),C.$nextFieldId.val(i.data.field.id+1),wpf.initTooltips(),k.loadColorPickers(),k.toggleAllOptionGroups(),y.trigger("wpformsFieldAdd",[i.data.field.id,s]))}else wpf.debug("Add field AJAX call is unsuccessful:",i)}).fail(function(e,i,o){a=!1,wpf.debug("Add field AJAX call failed:",e.responseText)}).always(function(){a||(WPForms.Admin.Builder.DragFields.enableDragAndDrop(),k.enableFormActions())});k.addEntryPreviewField(s,n)}},fieldAddToBaseLevel(e,i,o){var t,r=C.$sortableFieldsWrap,s=r.children(":not(.wpforms-field-drag-pending, .no-fields-preview)"),n=s.length,l=C.$fieldOptions;"top"===e.position?(r.prepend(i),l.prepend(o)):(t=s.last(),"bottom"!==e.position||t.length&&t.hasClass("wpforms-field-stick")?("bottom"===e.position&&(e.position=n),e.position===n&&t.length&&t.hasClass("wpforms-field-stick")?(n=t.data("field-id"),t.before(i),l.find("#wpforms-field-option-"+n).before(o)):(t=s.eq(e.position)).length?(n=t.data("field-id"),t.before(i),l.find("#wpforms-field-option-"+n).before(o)):(r.append(i),l.append(o))):(r.append(i),l.append(o)))},scrollPreviewToField(e){var e=v("#wpforms-field-"+e),i=C.$fieldsPreviewWrap.scrollTop(),o=e.closest(".wpforms-field-layout");let t=e.position().top;e=(t=o.length?o.position().top+t+20:t)>i?t-i:t+i;C.$fieldsPreviewWrap.scrollTop(e)},captchaUpdate(){var e={action:"wpforms_update_field_captcha",id:m.formID,nonce:wpforms_builder.nonce};return v.post(wpforms_builder.ajax_url,e,function(i){if(i.success){const o={title:!1,content:!1,icon:"fa fa-exclamation-circle",type:"orange",boxWidth:"450px",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}},t=v("#wpforms-panel-field-settings-recaptcha");let e=i.data.current;t.data("provider",i.data.provider),"configured_not_enabled"!==e&&"configured_enabled"!==e||(e=t.prop("checked")?"configured_enabled":"configured_not_enabled",o.buttons.confirm.action=function(){t.prop("checked","configured_not_enabled"===e).trigger("change")}),o.title=i.data.cases[e].title,o.content=i.data.cases[e].content,i.data.cases[e].cancel&&(o.buttons.cancel={text:wpforms_builder.cancel,keys:["esc"]}),v.confirm(o)}else console.log(i)}).fail(function(e,i,o){console.log(e.responseText)})},disableDragAndDrop(){console.warn('WARNING! Function "WPFormsBuilder.disableDragAndDrop()" has been deprecated, please use the new "WPForms.Admin.Builder.DragFields.disableDragAndDrop()" function instead!'),WPForms.Admin.Builder.DragFields.disableDragAndDrop()},enableDragAndDrop(){console.warn('WARNING! Function "WPFormsBuilder.enableDragAndDrop()" has been deprecated, please use the new "WPForms.Admin.Builder.DragFields.enableDragAndDrop()" function instead!'),WPForms.Admin.Builder.DragFields.enableDragAndDrop()},disableFormActions(){v.each([C.$previewButton,C.$embedButton,C.$saveButton,C.$exitButton],function(e,i){i.prop("disabled",!0).addClass("wpforms-disabled")})},enableFormActions(){v.each([C.$previewButton,C.$embedButton,C.$saveButton,C.$exitButton],function(e,i){i.prop("disabled",!1).removeClass("wpforms-disabled")})},fieldSortable(){console.warn('WARNING! Function "WPFormsBuilder.fieldSortable()" has been deprecated, please use the new "WPForms.Admin.Builder.DragFields.initSortableFields()" function instead!'),WPForms.Admin.Builder.DragFields.initSortableFields()},fieldDragDisable(e,i=!0){console.warn('WARNING! Function "WPFormsBuilder.fieldDragDisable()" has been deprecated, please use the new "WPForms.Admin.Builder.DragFields.fieldDragDisable()" function instead!'),WPForms.Admin.Builder.DragFields.fieldDragDisable(e,i)},fieldDragEnable(e){console.warn('WARNING! Function "WPFormsBuilder.fieldDragEnable()" has been deprecated, please use the new "WPForms.Admin.Builder.DragFields.fieldDragEnable()" function instead!'),WPForms.Admin.Builder.DragFields.fieldDragEnable(e)},fieldChoiceAdd(e,i){e.preventDefault();var e=v(i),i=e.parent(),o=i.find("input.default").is(":checked"),e=e.closest(".wpforms-field-option-row-choices").data("field-id"),t=i.parent().attr("data-next-id"),r=i.parent().data("field-type"),s=i.clone().insertAfter(i);s.attr("data-key",t),s.find("input.label").val("").attr("name","fields["+e+"][choices]["+t+"][label]"),s.find("input.value").val("").attr("name","fields["+e+"][choices]["+t+"][value]"),s.find(".wpforms-image-upload input.source").val("").attr("name","fields["+e+"][choices]["+t+"][image]"),s.find(".wpforms-icon-select input.source-icon").val(wpforms_builder.icon_choices.default_icon).attr("name","fields["+e+"][choices]["+t+"][icon]"),s.find(".wpforms-icon-select input.source-icon-style").val(wpforms_builder.icon_choices.default_icon_style).attr("name","fields["+e+"][choices]["+t+"][icon_style]"),s.find(".wpforms-icon-select .ic-fa-preview").removeClass().addClass("ic-fa-preview ic-fa-"+wpforms_builder.icon_choices.default_icon_style+" ic-fa-"+wpforms_builder.icon_choices.default_icon),s.find(".wpforms-icon-select .ic-fa-preview + span").text(wpforms_builder.icon_choices.default_icon),s.find("input.default").attr("name","fields["+e+"][choices]["+t+"][default]").prop("checked",!1),s.find(".preview").empty(),s.find(".wpforms-image-upload-add").show(),s.find(".wpforms-money-input").trigger("focusout"),!0===o&&i.find("input.default").prop("checked",!0),t++,i.parent().attr("data-next-id",t),y.trigger("wpformsFieldChoiceAdd",[e]),k.fieldChoiceUpdate(r,e)},fieldChoiceDelete(e,i){e.preventDefault();const o=v(i),t=o.parent().parent(),r=t.find("li").length,s={id:t.data("field-id"),choiceId:o.closest("li").data("key"),message:""+wpforms_builder.delete_choice_confirm+"",trigger:!1};if(y.trigger("wpformsBeforeFieldDeleteAlert",[s]),1===r)k.fieldChoiceDeleteAlert();else{const n=function(){o.parent().remove(),k.fieldChoiceUpdate(t.data("field-type"),t.data("field-id")),y.trigger("wpformsFieldChoiceDelete",[t.data("field-id")])};s.trigger?v.confirm({title:!1,content:s.message,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"],action(){n()}},cancel:{text:wpforms_builder.cancel}}}):n()}},fieldChoiceDeleteAlert(){v.alert({title:!1,content:wpforms_builder.error_choice,icon:"fa fa-info-circle",type:"blue",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}})},fieldChoiceSortable(t,e=void 0){v(e=void 0!==e?e:".wpforms-field-option-"+t+" .wpforms-field-option-row-choices ul").sortable({items:"li",axis:"y",delay:100,opacity:.6,handle:".move",stop(e,i){var o=i.item.parent().data("field-id");k.fieldChoiceUpdate(t,o),y.trigger("wpformsFieldChoiceMove",i)},update(e,i){}})},fieldChoiceLabel(e,i){var o=["payment-multiple","payment-checkbox"].includes(e.settings.type),t=e.settings.choices_icons||e.settings.choices_images,r=void 0===e.settings.choices[i].label||0===e.settings.choices[i].label.length;if(r&&!o&&t)return"";t=o?wpforms_builder.payment_choice_empty_label_tpl:wpforms_builder.choice_empty_label_tpl;let s=r?t.replace("{number}",i):wpf.sanitizeHTML(e.settings.choices[i].label,wpforms_builder.allowed_label_html_tags);return e.settings.show_price_after_labels&&(s+=" - "+wpf.amountFormatCurrency(e.settings.choices[i].value)),s},fieldChoiceUpdate:(n,e,i=-1)=>{const l=k.dropdownField.helpers.isDynamicChoices(e);if(!k.replaceChoicesWithTemplate(n,e,l)){-1===i&&(i=k.settings.choicesLimitLong),"payment-select"===n&&(n="select");const a=v("#wpforms-field-"+e+" .primary-input");let s="";"select"===n?l||(s='',a.find("option").not(".placeholder").remove()):"radio"!==n&&"checkbox"!==n&&"gdpr-checkbox"!==n||(n="gdpr-checkbox"===n?"checkbox":n,a.find("li").remove(),s='
  • {label}
  • ');const t=v("#wpforms-field-option-row-"+e+"-choices .choices-list"),r=t.find("li").slice(0,i),d=!!t.find("input.default:checked").length,p=[],f=v("#wpforms-field-option-"+e+"-show_price_after_labels").prop("checked"),c=k.dropdownField.helpers.isModernSelect(a);var o;r.get().forEach(function(e){var e=v(e),i=e.find("input.value").val(),o=e.data("key");let t=wpf.sanitizeHTML(e.find("input.label").val().trim(),wpforms_builder.allowed_label_html_tags),r;if(t=""!==t?t:wpforms_builder.choice_empty_label_tpl.replace("{number}",o),t+=f&&i?" - "+wpf.amountFormatCurrency(i):"",c?p.push({value:t,label:t}):l||(r=v(s.replace(/{label}/g,t)),a.append(r)),!0===e.find("input.default").is(":checked"))switch(n){case"select":c?p[p.length-1].selected=!0:k.setClassicSelectedChoice(r);break;case"radio":case"checkbox":r.find("input").prop("checked","true")}}),c&&(i=a.prop("multiple")?"input.choices__input":".choices__inner .choices__placeholder",o=k.dropdownField.helpers.getInstance(a),l||o.removeActiveItems(),o.setChoices(p,"value","label",!0),k.dropdownField.helpers.update(e,l),a.closest(".choices").find(i).toggleClass("wpforms-hidden",d))}},replaceChoicesWithTemplate:(e,i,o)=>{if("radio"!==e&&"checkbox"!==e&&"payment-multiple"!==e&&"payment-checkbox"!==e)return!1;var t=wpf.getChoicesOrder(i),r=wp.template("wpforms-field-preview-checkbox-radio-payment-multiple");const s=wpf.getField(i),n={},l=t.slice(0,k.settings.choicesLimit),a={settings:s,order:l,type:"radio"};return s.choices_icons&&(a.settings.choices_icons_color=k.getValidColorPickerValue(v("#wpforms-field-option-"+i+"-choices_icons_color"))),l.forEach(function(e){n[e]=s.choices[e]}),s.choices=n,"checkbox"!==e&&"payment-checkbox"!==e||(a.type="checkbox"),o||v("#wpforms-field-"+i).find("ul.primary-input").replaceWith(r(a)),k.firstNChoicesAlert(i,t.length),!0},setClassicSelectedChoice(e){void 0!==e&&e.prop("selected","true")},fieldChoiceBulkAddToggle(i){var i=v(i),o=i.closest("label");if(i.hasClass("bulk-add-showing")){const e=o.next(".bulk-add-display");e.slideUp(400,function(){e.remove()}),i.find("span").text(wpforms_builder.bulk_add_show)}else{let e='
    ';e=e+('

    '+wpforms_builder.bulk_add_heading+' '+wpforms_builder.bulk_add_presets_show+"

    ")+"")+(''))+('")+"
    ",o.after(e),o.next(".bulk-add-display").slideDown(400,function(){v(this).find("textarea").trigger("focus")}),i.find("span").text(wpforms_builder.bulk_add_hide)}i.toggleClass("bulk-add-showing")},fieldChoiceBulkAddInsert(e){var i,e=v(e),o=e.closest(".wpforms-field-option-row"),t=o.find("textarea"),r=o.find(".choices-list"),s=r.find("li:first-of-type").clone().wrap("
    ").parent(),n=o.data("field-id"),l=r.data("field-type");let a=Number(r.attr("data-next-id"));var d=t.val().split("\n");let p="";e.prop("disabled",!0).html(e.html()+" "+m.spinner),s.find("input.value,input.label").attr("value",""),s.find("input.default").attr("checked",!1),s.find("input.source-icon").attr("value",wpforms_builder.icon_choices.default_icon),s.find("input.source-icon-style").attr("value",wpforms_builder.icon_choices.default_icon_style),s.find(".ic-fa-preview").removeClass().addClass(`ic-fa-preview ic-fa-${wpforms_builder.icon_choices.default_icon_style} ic-fa-`+wpforms_builder.icon_choices.default_icon),s.find(".ic-fa-preview + span").text(wpforms_builder.icon_choices.default_icon),i=s.html();for(const c in d)if(d.hasOwnProperty(c)){var f=wpf.sanitizeHTML(d[c]).trim().replace(/"/g,""");let e=i;e=(e=(e=(e=e.replace(/\[choices\]\[(\d+)\]/g,"[choices]["+a+"]")).replace(/data-key="(\d+)"/g,'data-key="'+a+'"')).replace(/value="" class="label"/g,'value="'+f+'" class="label"')).replace(/class="label" type="text" value=""/g,'class="label" type="text" value="'+f+'"'),p+=e,a++}r.attr("data-next-id",a).append(p),k.fieldChoiceUpdate(l,n,a),y.trigger("wpformsFieldChoiceAdd"),k.fieldChoiceBulkAddToggle(o.find(".toggle-bulk-add-display"))},triggerBuilderEvent(e){y.trigger(e)},fieldTabToggle(e){var i;if(WPFormsUtils.triggerEvent(y,"wpformsFieldTabToggle",[e]).isDefaultPrevented())return!1;v(".wpforms-tab a").removeClass("active"),v(".wpforms-field, .wpforms-title-desc").removeClass("active"),"add-fields"===e?(C.$addFieldsTab.addClass("active"),v(".wpforms-field-options").hide(),v(".wpforms-add-fields").show()):(v("#field-options a").addClass("active"),"field-options"===e?((i=v(".wpforms-field").first()).addClass("active"),e=i.data("field-id")):v("#wpforms-field-"+e).addClass("active"),v(".wpforms-field-option").hide(),v("#wpforms-field-option-"+e).show(),v(".wpforms-add-fields").hide(),v(".wpforms-field-options").show(),y.trigger("wpformsFieldOptionTabToggle",[e]))},fieldPagebreakAdd(e,i,o){if("pagebreak"===o){let e;m.pagebreakTop?m.pagebreakBottom||(m.pagebreakBottom=!0,e={position:"bottom",scroll:!1,defaults:{position:"bottom"}},k.fieldAdd("pagebreak",e).done(function(e){m.pagebreakBottom=e.data.field.id;var i=v("#wpforms-field-"+e.data.field.id);v("#wpforms-field-option-"+e.data.field.id).find(".wpforms-field-option-group").addClass("wpforms-pagebreak-bottom"),i.addClass("wpforms-field-stick wpforms-pagebreak-bottom")})):(m.pagebreakTop=!0,e={position:"top",scroll:!1,defaults:{position:"top",nav_align:"left"}},k.fieldAdd("pagebreak",e).done(function(e){m.pagebreakTop=e.data.field.id;var i=v("#wpforms-field-"+e.data.field.id);v("#wpforms-field-option-"+e.data.field.id).find(".wpforms-field-option-group").addClass("wpforms-pagebreak-top"),i.addClass("wpforms-field-stick wpforms-pagebreak-top")}))}},fieldPagebreakDelete(e,i,o){var t,r,s;"pagebreak"!==o||v("#wpforms-panel-fields .wpforms-field-pagebreak").not(".wpforms-pagebreak-top, .wpforms-pagebreak-bottom").length||(r=(t=(o=v("#wpforms-panel-fields .wpforms-preview-wrap")).find(".wpforms-pagebreak-top")).data("field-id"),s=(o=o.find(".wpforms-pagebreak-bottom")).data("field-id"),t.remove(),v("#wpforms-field-option-"+r).remove(),m.pagebreakTop=!1,o.remove(),v("#wpforms-field-option-"+s).remove(),m.pagebreakBottom=!1)},fieldPageBreakInitDisplayPrevious(e){var i=e.data("field-id"),o=v("#wpforms-field-option-row-"+i+"-prev_toggle"),i=v("#wpforms-field-option-row-"+i+"-prev"),t=e.find(".wpforms-pagebreak-prev");0":">")+f[e].label+"",c.append(t)}else for(e in i="radio",n.hasClass("wpforms-field-checkbox")&&(i="checkbox"),c.empty(),f)t='
  • ":">")+f[e].label+"
  • ",c.append(t);o.find("ul").removeClass("wpforms-hidden"),o.find(".wpforms-alert").addClass("wpforms-hidden"),c.removeClass("wpforms-hidden")}wpf.fieldOptionLoading(r,!0)}},fieldDynamicChoiceSource(e){const i=v(e),o=i.parent(),t=i.val(),r=o.data("field-id"),s=v("#wpforms-builder-form").data("id"),n=v("#wpforms-field-option-row-"+r+"-choices"),l=v("#wpforms-field-"+r),a=v("#wpforms-field-option-"+r+"-dynamic_choices option:selected").val();let d=20;wpf.fieldOptionLoading(o);e={type:a,source:t,field_id:r,form_id:s,action:"wpforms_builder_dynamic_source",nonce:wpforms_builder.nonce};v.post(wpforms_builder.ajax_url,e,function(i){if(i.success){if(n.find(".dynamic-name").text(i.data.source_name),n.find(".dynamic-type").text(i.data.type_name),n.find("ul").addClass("wpforms-hidden"),n.find(".wpforms-alert").removeClass("wpforms-hidden"),k.fieldDynamicChoiceSourceItems(l,i.data.items),l.hasClass("wpforms-field-select")&&(d=200),l.find(".wpforms-notice-dynamic-empty").remove(),Number(i.data.total)>d){let e=wpforms_builder.dynamic_choices.limit_message;e=(e=(e=(e=e.replace("{source}",i.data.source_name)).replace("{type}",i.data.type_name)).replace("{limit}",d)).replace("{total}",i.data.total),v.alert({title:wpforms_builder.heads_up,content:e,icon:"fa fa-info-circle",type:"blue",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}})}k.firstNChoicesAlert(r,i.data.total),0===Number(i.data.total)&&k.emptyChoicesNotice(r,i.data.source_name,i.data.type)}else console.log(i);wpf.fieldOptionLoading(o,!0)}).fail(function(e,i,o){console.log(e.responseText)})},fieldDynamicChoiceSourceItems(i,o){var t=i.find(".primary-input");let r=0;if(i.hasClass("wpforms-field-select"))k.dropdownField.helpers.isModernSelect(t)?k.fieldDynamicChoiceSourceForModernSelect(t,o):k.fieldDynamicChoiceSourceForClassicSelect(t,o);else{let e="radio";for(r in i.hasClass("wpforms-field-checkbox")&&(e="checkbox"),t.empty(),o)t.append('
  • '+wpf.sanitizeHTML(o[r])+"
  • ")}},fieldDynamicChoiceSourceForModernSelect(e,i){var o=k.dropdownField.helpers.getInstance(e),t=e.closest(".wpforms-field").data().fieldId;o.destroy(),k.dropdownField.helpers.updatePlaceholderChoice(o,t),k.fieldDynamicChoiceSourceForClassicSelect(e,i),k.dropdownField.events.choicesInit(e)},fieldDynamicChoiceSourceForClassicSelect(e,i){let o=0;var t=i.length;for(e.find("option").not(".placeholder").remove();o'+r+"")}e.toggleClass("wpforms-hidden",!t)},fieldDynamicChoiceToggleImageChoices(){v("#wpforms-builder .wpforms-field-options .wpforms-field-option").each(function(e,i){var i=v(i),o=i.find(".wpforms-field-option-row-dynamic_choices select").val(),o=void 0!==o&&""!==o,t=i.find(".wpforms-field-option-row-choices_images input").is(":checked");i.find(".wpforms-field-option-row-choices_images").toggleClass("wpforms-hidden",o),t&&!o||i.find(".wpforms-field-option-row-choices_images_style").addClass("wpforms-hidden")})},fieldDynamicChoiceToggleIconChoices(){v("#wpforms-builder .wpforms-field-options .wpforms-field-option").each(function(e,i){var i=v(i),o=i.find(".wpforms-field-option-row-dynamic_choices select").val(),o=void 0!==o&&""!==o,t=i.find(".wpforms-field-option-row-choices_icons input").is(":checked");i.find(".wpforms-field-option-row-choices_icons").toggleClass("wpforms-hidden",o),t&&!o||(i.find(".wpforms-field-option-row-choices_icons_color").addClass("wpforms-hidden"),i.find(".wpforms-field-option-row-choices_icons_size").addClass("wpforms-hidden"),i.find(".wpforms-field-option-row-choices_icons_style").addClass("wpforms-hidden"))})},firstNChoicesAlert:(e,i)=>{var o,t,r,e=v("#wpforms-field-"+e);e.hasClass("wpforms-field-select")||(o=wp.template("wpforms-choices-limit-message"),t={total:i},r=k.settings.choicesLimit,e.find(".wpforms-alert-dynamic").remove(),r .layout-selector-display`);if(!e.length){var t={"layout-1":[{class:"one-half",data:"wpforms-one-half wpforms-first"},{class:"one-half",data:"wpforms-one-half"}],"layout-2":[{class:"one-third",data:"wpforms-one-third wpforms-first"},{class:"one-third",data:"wpforms-one-third"},{class:"one-third",data:"wpforms-one-third"}],"layout-3":[{class:"one-fourth",data:"wpforms-one-fourth wpforms-first"},{class:"one-fourth",data:"wpforms-one-fourth"},{class:"one-fourth",data:"wpforms-one-fourth"},{class:"one-fourth",data:"wpforms-one-fourth"}],"layout-4":[{class:"one-third",data:"wpforms-one-third wpforms-first"},{class:"two-third",data:"wpforms-two-thirds"}],"layout-5":[{class:"two-third",data:"wpforms-two-thirds wpforms-first"},{class:"one-third",data:"wpforms-one-third"}],"layout-6":[{class:"one-fourth",data:"wpforms-one-fourth wpforms-first"},{class:"one-fourth",data:"wpforms-one-fourth"},{class:"two-fourth",data:"wpforms-two-fourths"}],"layout-7":[{class:"two-fourth",data:"wpforms-two-fourths wpforms-first"},{class:"one-fourth",data:"wpforms-one-fourth"},{class:"one-fourth",data:"wpforms-one-fourth"}],"layout-8":[{class:"one-fourth",data:"wpforms-one-fourth wpforms-first"},{class:"two-fourth",data:"wpforms-two-fourths"},{class:"one-fourth",data:"wpforms-one-fourth"}]};let e,i=`

    ${wpforms_builder.layout_selector_layout}

    `;for(const r in t){e=t[r],i+='
    ';for(const s in e)i+=``;i+="
    "}i+="
    ",v(`#wpforms-field-option-row-${o}-css > label`).after(i)}},fieldLayoutSelectorLayout(e){e=v(e);e.parent().find(".layout-selector-display-layout").not(e).remove(),e.parent().find(".heading").text(wpforms_builder.layout_selector_column),e.toggleClass("layout-selector-display-layout layout-selector-display-columns")},fieldLayoutSelectorInsert(e){const i=v(e),o=i.closest(".layout-selector-display"),t=o.parent(),r=t.data("field-id"),s=t.find("label"),n=t.find("input[type=text]");let l=i.data("classes"),a=n.val();a&&(["wpforms-one-half","wpforms-first","wpforms-one-third","wpforms-one-fourth","wpforms-two-thirds","wpforms-two-fourths"].forEach(e=>{a=a.replace(new RegExp("\\b"+e+"\\b","gi"),"")}),a=a.replace(/\s\s+/g," ").trim(),l+=" "+a),n.val(l),o.slideUp(400,function(){o.remove(),k.fieldLayoutSelectorInit(r)}),s.find(".toggle-layout-selector-display").removeClass("layout-selector-showing"),s.find(".toggle-layout-selector-display span").text(wpforms_builder.layout_selector_show)},toggleOrderSummaryConfirmation(e,i,o){"payment-total"===o&&v(".wpforms-confirmation").each(function(){v(this).find(".wpforms-panel-field-confirmations-message_order_summary").closest(".wpforms-panel-field").toggle(0!==v("#wpforms-panel-fields .wpforms-field-payment-total").length)})},bindUIActionsSettings(){y.on("click","#wpforms-panel-fields .wpforms-title-desc, #wpforms-panel-fields .wpforms-field-submit-button, .wpforms-center-form-name",function(e){e.preventDefault(),k.panelSwitch("settings"),(v(this).hasClass("wpforms-center-form-name")||v(this).hasClass("wpforms-title-desc"))&&setTimeout(function(){v("#wpforms-panel-field-settings-form_title").trigger("focus")},300)}),y.on("click",".wpforms-field-pagebreak-last button",function(e){e.preventDefault(),k.panelSwitch("settings"),v("#wpforms-panel-field-settings-pagebreak_prev").trigger("focus")}),y.on("click",".wpforms-panel-content-also-available-item-add-captcha",function(e){e.preventDefault();e=y.find("#wpforms-add-fields-captcha");e.data("action")?e.trigger("click"):k.fieldAdd("captcha",{}).done(function(e){k.panelSwitch("fields"),v("#wpforms-field-"+e.data.field.id).trigger("click")})}),y.on("input","#wpforms-panel-field-settings-pagebreak_prev",function(){v(".wpforms-field-pagebreak-last button").text(v(this).val())}),y.on("input","#wpforms-panel-field-settings-form_title, #wpforms-setup-name",function(){var e=v(this).val().toString().trim();v(".wpforms-preview .wpforms-form-name").text(e),v(".wpforms-center-form-name.wpforms-form-name").text(e),k.trimFormTitle()}),y.on("input","#wpforms-panel-field-settings-form_desc",function(){v(".wpforms-form-desc").text(v(this).val())}),y.on("input","#wpforms-panel-field-settings-submit_text",function(){var e=v(this).val()||wpforms_builder.submit_text;v(".wpforms-field-submit input[type=submit]").val(e)}),y.on("change","#wpforms-panel-field-settings-recaptcha",function(){k.captchaToggle()}),y.on("change",".wpforms-panel-field-confirmations-type",function(){k.confirmationFieldsToggle(v(this))}),y.on("change",".wpforms-panel-field-confirmations-message_entry_preview",k.confirmationEntryPreviewToggle),y.on("change","#wpforms-panel-field-settings-notification_enable",k.notificationToggle),y.on("click",".wpforms-builder-settings-block-add",function(e){e.preventDefault(),wpforms_builder.pro&&k.settingsBlockAdd(v(this))}),y.on("click",".wpforms-builder-settings-block-edit",function(e){e.preventDefault();e=v(this);e.parents(".wpforms-builder-settings-block-header").find(".wpforms-builder-settings-block-name").hasClass("editing")?k.settingsBlockNameEditingHide(e):k.settingsBlockNameEditingShow(e)}),y.on("blur",".wpforms-builder-settings-block-name-edit input",function(e){v(e.relatedTarget).hasClass("wpforms-builder-settings-block-edit")||k.settingsBlockNameEditingHide(v(this))}),y.on("keypress",".wpforms-builder-settings-block-name-edit input",function(e){13===e.keyCode&&(k.settingsBlockNameEditingHide(v(this)),e.preventDefault())}),y.on("click",".wpforms-builder-settings-block-clone",function(e){e.preventDefault(),k.settingsBlockPanelClone(v(this))}),y.on("click",".wpforms-builder-settings-block-toggle",function(e){e.preventDefault(),k.settingsBlockPanelToggle(v(this))}),y.on("click",".wpforms-builder-settings-block-delete",function(e){e.preventDefault(),k.settingsBlockDelete(v(this))}),y.on("wpformsSettingsBlockAdded wpformsSettingsBlockCloned",function(e,i){i.hasClass("wpforms-notification")&&(k.notificationsUpdateElementsVisibility(),k.notificationUpdateStatus(i))}),y.on("wpformsSettingsBlockDeleted",function(e,i){"notification"===i&&k.notificationsUpdateElementsVisibility()}),y.on("change",".js-wpforms-enabled-notification input",function(){k.notificationUpdateStatus(v(this).closest(".wpforms-notification"))}),y.on("change","#wpforms-panel-field-settings-ajax_submit",function(){k.hideOpenConfirmationsInNewTabOptions(!v(this).is(":checked"))})},hideOpenConfirmationsInNewTabOptions(e){var i=y.find(".wpforms-panel-content-section-confirmation").find(".wpforms-builder-settings-block").find(".wpforms-panel-field-confirmations-redirect_new_tab");i.length&&i.each(function(){v(this).closest(".wpforms-panel-field").toggle(!e)})},captchaToggle(){var e=y.find(".wpforms-field-recaptcha"),i=v("#wpforms-panel-field-settings-recaptcha"),o=i.data("provider")||"recaptcha";e.length&&(i.is(":checked")?e.show().toggleClass("is-recaptcha","recaptcha"===o):e.hide())},initConfirmationsType(){v(".wpforms-panel-field-confirmations-type").each(function(){k.confirmationFieldsToggle(v(this))})},initElementsTinyMCE(e){"undefined"!=typeof tinymce&&void 0!==wp.editor&&e.each(function(){var e=v(this).attr("id");wp.editor.remove(e),wp.editor.initialize(e,m.tinymceDefaults)})},confirmationsSetup(){k.initConfirmationsType(),k.initElementsTinyMCE(v(".wpforms-panel-field-confirmations-message")),y.on("focusout",".wpforms-panel-field-confirmations-redirect",function(e){const i=v(this);var o=i.val().trim();i.val(o),wpf.isURL(o)||""===o||k.confirmationRedirectValidationError(function(){i.trigger("focus")})}),y.on("wpformsBeforeSave wpformsPanelSectionSwitch wpformsPanelSwitch",function(t){v(".wpforms-confirmation").each(function(e,i){i=v(i);const o=i.find(".wpforms-panel-field-confirmations-redirect");if(!o.is(":hidden")){i=i.find(".wpforms-panel-field-confirmations-type");if(!("redirect"!==i.val()||0',o='

    '+wpforms_builder[f+"_error"]+"

    ",t=e+i+o,r=v.confirm({container:y,title:!1,content:t,icon:"fa fa-info-circle",type:"blue",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"],action(){var e=this.$content.find("input#settings-block-name").val().toString().trim(),i=this.$content.find(".error");if(""===e)return i.show(),!1;var i=a.closest(".wpforms-panel-content-section").find(".wpforms-builder-settings-block").first(),o=(wpf.restoreTooltips(i),i.clone()),t=i.data("block-id");let r;o.attr("data-block-id",d),o.find(".wpforms-builder-settings-block-header span").text(e);o.find("input, textarea, select").each(function(){var e=v(this),i=e.parent();e.hasClass("wpforms-disabled")&&(i.hasClass("from-name")||i.hasClass("from-email"))||(i=e).attr("name")&&(i.val("").attr("name",i.attr("name").replace(/\[(\d+)\]/,"["+d+"]")),i.is("select")?(i.find("option").prop("selected",!1).attr("selected",!1),i.find("option").first().prop("selected",!0).attr("selected","selected")):"checkbox"===i.attr("type")?(e=i.closest(".wpforms-panel-field").hasClass("js-wpforms-enabled-notification"),i.prop("checked",e).attr("checked",e).val("1")):i.val("").attr("value",""))});const s="wpforms-panel-field-"+p+"-",n=s+t,l=(o.find('[id^="'+n+'"], [for^="'+n+'"]').each(function(e,i){var o=v(this),t="LABEL"===o.prop("tagName")?"for":"id",r=o.attr(t).replace(new RegExp(n,"g"),s+d);o.attr(t,r)}),t+"-notification-by-status");o.find('[data-radio-group="'+l+'"]').each(function(e,i){v(this).removeClass("wpforms-radio-group-"+l).addClass("wpforms-radio-group-"+d+"-notification-by-status").attr("data-radio-group",d+"-notification-by-status")}),o.find(".wpforms-builder-settings-block-header input").val(e).attr("value",e),"notification"===f&&(o.find(".email-msg textarea").val("{all_fields}").attr("value","{all_fields}"),o.find(".email-recipient input").val("{admin_email}").attr("value","{admin_email}")),o.removeClass("wpforms-builder-settings-block-default"),"confirmation"===f&&(o.find(".wpforms-panel-field-tinymce").remove(),"undefined"!=typeof WPForms)&&o.find(".wpforms-panel-field-confirmations-type-wrap").after(WPForms.Admin.Builder.Templates.get("wpforms-builder-confirmations-message-field")({id:d}));t=o.find(".wpforms-conditional-block"),t.length&&"undefined"!=typeof WPForms&&t.html(WPForms.Admin.Builder.Templates.get("wpforms-builder-conditional-logic-toggle-field")({id:d,type:f,actions:JSON.stringify(o.find(".wpforms-panel-field-conditional_logic-checkbox").data("actions")),actionDesc:o.find(".wpforms-panel-field-conditional_logic-checkbox").data("action-desc"),reference:o.find(".wpforms-panel-field-conditional_logic-checkbox").data("reference")})),e=o.find(".wpforms-field-map-table"),e.length&&e.each(function(e,i){var i=v(i),o=(i.find("tr:not(:first-child)").remove(),i.find(".key input")),i=i.find(".field select"),t=i.data("name");o.attr("value",""),i.attr("name","").attr("data-name",t.replace(/\[(\d+)\]/,"["+d+"]"))}),r=(r=o.wrap("
    ").parent().html()).replace(/\[conditionals\]\[(\d+)\]\[(\d+)\]/g,"[conditionals][0][0]"),i.before(r),t=i.prev();"confirmation"===f&&(k.prepareChoicesJSField(t,d),k.confirmationFieldsToggle(v(".wpforms-panel-field-confirmations-type").first())),"undefined"!=typeof tinymce&&void 0!==wp.editor&&"confirmation"===f&&wp.editor.initialize("wpforms-panel-field-confirmations-message-"+d,m.tinymceDefaults),wpf.initTooltips(),y.trigger("wpformsSettingsBlockAdded",[t]),a.attr("data-next-id",d+1)}},cancel:{text:wpforms_builder.cancel}}});y.on("keypress","#settings-block-name",function(e){13===e.keyCode&&v(r.buttons.confirm.el).trigger("click")})},prepareChoicesJSField(e,i){var o,e=e.find(`#wpforms-panel-field-confirmations-${i}-page-wrap`);e.length<=0||(i=e.find(`#wpforms-panel-field-confirmations-${i}-page`)).length<=0&&!i.hasClass("choicesjs-select")||(o=e.find(".choices")).length<=0||((i=i.first()).removeAttr("data-choice"),i.removeAttr("hidden"),i.removeClass("choices__input"),v(i).appendTo(e.first()),o.first().remove(),k.dropdownField.events.choicesInit(i))},settingsBlockNameEditingShow(e){e=e.parents(".wpforms-builder-settings-block-header");e.find(".wpforms-builder-settings-block-name").addClass("editing").hide(),e.find(".wpforms-builder-settings-block-name-edit").addClass("active"),wpf.focusCaretToEnd(e.find("input"))},settingsBlockNameEditingHide(e){var i=e.parents(".wpforms-builder-settings-block-header"),o=i.find(".wpforms-builder-settings-block-name"),i=i.find(".wpforms-builder-settings-block-name-edit");let t=i.find("input").val().trim();e=e.closest(".wpforms-builder-settings-block").data("block-type");t.length||(t=wpforms_builder[e+"_def_name"]),i.find("input").val(t),o.text(t),o.removeClass("editing").show(),i.removeClass("active")},settingsBlockPanelClone(e){const i=e.closest(".wpforms-panel-content-section"),o=i.find(".wpforms-builder-settings-block-add"),t=e.closest(".wpforms-builder-settings-block"),r=t.find(".wpforms-builder-settings-block-content"),s=parseInt(o.attr("data-next-id"),10),n=t.data("block-type"),l=t.find(".wpforms-builder-settings-block-name").text().trim()+wpforms_builder[n+"_clone"],a=r.is(":hidden"),d=(wpf.restoreTooltips(t),t.clone(!1,!0));k.settingsBlockUpdateState(a,s,n),d.data("block-id",s),d.find(".wpforms-builder-settings-block-header span").text(l),d.find(".wpforms-builder-settings-block-header input").val(l),d.removeClass("wpforms-builder-settings-block-default"),o.attr("data-next-id",s+1),d.find("input, textarea, select").each(function(){var e=v(this);e.attr("name")&&e.attr("name",e.attr("name").replace(/\[(\d+)\]/,"["+s+"]")),e.data("name")&&e.data("name",e.data("name").replace(/\[(\d+)\]/,"["+s+"]")),e.attr("class")&&e.attr("class",e.attr("class").replace(/-(\d+)/,"-"+s)),e.attr("data-radio-group")&&e.attr("data-radio-group",e.attr("data-radio-group").replace(/(\d+)-/,s+"-"))}),d.find("*").each(function(){var e=v(this);e.attr("id")&&e.attr("id",e.attr("id").replace(/-(\d+)/,"-"+s)),e.attr("for")&&e.attr("for",e.attr("for").replace(/-(\d+)-/,"-"+s+"-")),e.data("input-name")&&e.data("input-name",e.data("input-name").replace(/\[(\d+)\]/,"["+s+"]"))}),t.find("select").each(function(){var e=v(this).attr("name"),i=v(this).attr("name").replace(/\[(\d+)\]/,"["+s+"]");d.find('select[name="'+i+'"]').val(v(this).attr("name",e).val())}),d.css("display","none").insertBefore(t).show("fast",function(){wpf.initTooltips()}),y.trigger("wpformsSettingsBlockCloned",[d,t.data("block-id")])},settingsBlockPanelToggle(e){const i=e.closest(".wpforms-builder-settings-block"),o=i.data("block-id"),t=i.data("block-type"),r=i.find(".wpforms-builder-settings-block-content"),s=r.is(":visible");r.stop().slideToggle({duration:400,start(){k.settingsBlockUpdateState(s,o,t)},always(){r.is(":visible")?e.html(''):e.html('')}})},settingsBlockDelete(e){var i=e.closest(".wpforms-panel-content-section");if(!(i.find(".wpforms-builder-settings-block").length<2)){const o=e.closest(".wpforms-builder-settings-block"),t=o.data("block-type");v.confirm({title:!1,content:wpforms_builder[t+"_delete"],icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"],action(){var e=o.data("block-id"),i=o.data("block-type");v.post(wpforms_builder.ajax_url,{action:"wpforms_builder_settings_block_state_remove",nonce:wpforms_builder.nonce,block_id:e,block_type:i,form_id:m.formID}),o.remove(),y.trigger("wpformsSettingsBlockDeleted",[t,e])}},cancel:{text:wpforms_builder.cancel}}})}},settingsBlockUpdateState(e,i,o){v.post(wpforms_builder.ajax_url,{action:"wpforms_builder_settings_block_state_save",state:e?"closed":"opened",form_id:m.formID,block_id:i,block_type:o,nonce:wpforms_builder.nonce})},notificationsUpdateElementsVisibility(){var e=v(".wpforms-panel-content-section-notifications .wpforms-notification"),i=e.find(".wpforms-builder-settings-block-status"),o=1"+e+"

    "+wpforms_builder.error_contact_support+"

    ",icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}})},formExit(){k.isBuilderInPopup()&&k.formIsSaved()?k.builderInPopupClose("saved"):k.formIsSaved()?l.location.href=wpforms_builder.exit_url:v.confirm({title:!1,content:wpforms_builder.exit_confirm,icon:"fa fa-exclamation-circle",type:"orange",closeIcon:!0,buttons:{confirm:{text:wpforms_builder.save_exit,btnClass:"btn-confirm",keys:["enter"],action(){k.formSave(!0)}},cancel:{text:wpforms_builder.exit,action(){o=!1,k.isBuilderInPopup()?k.builderInPopupClose("canceled"):l.location.href=wpforms_builder.exit_url}}}})},setCloseConfirmation(e){o=!!e},formIsSaved(){return wpf.savedState===wpf.getFormState("#wpforms-builder-form")},isBuilderInPopup(){return l.self!==l.parent&&"wpforms-builder-iframe"===l.self.frameElement.id},builderInPopupClose(e){var i=l.parent.jQuery(".wpforms-builder-popup"),o=v(".wpforms-center-form-name").text();i.find("#wpforms-builder-iframe").attr("src","about:blank"),i.fadeOut(),i.trigger("wpformsBuilderInPopupClose",[e,m.formID,o])},bindUIActionsGeneral(){y.on("click",".toggle-smart-tag-display",k.smartTagToggle),y.on("click",".smart-tags-list-display a",k.smartTagInsert),y.on("click",".wpforms-panel-fields-group.unfoldable .wpforms-panel-fields-group-title",k.toggleUnfoldableGroup),y.on("click",".wpforms-field-helper-hide ",k.hideFieldHelper),y.on("input",".wpforms-field-map-table .key-source",function(){var e=v(this).val(),i=v(this).parent().parent().find(".key-destination"),o=i.data("name");e&&i.attr("name",o.replace("{source}",e.replace(/[^0-9a-zA-Z_-]/gi,"")))}),y.on("click",".wpforms-field-map-table .remove",function(e){e.preventDefault(),k.fieldMapTableDeleteRow(e,v(this))}),y.on("click",".wpforms-field-map-table .add",function(e){e.preventDefault(),k.fieldMapTableAddRow(e,v(this))}),v(r).on("wpformsFieldUpdate",k.fieldMapSelect),y.on("input",".wpforms-money-input",function(e){var i=v(this),o=i.val(),t=i[0].selectionStart,r=i[0].selectionEnd;i.val(o.replace(/[^0-9.,]/g,"")),i[0].setSelectionRange(t,r)}),y.on("focusout",".wpforms-money-input",function(e){var i=v(this),o=i.val();if(!o)return o;o=wpf.amountSanitize(o),o=wpf.amountFormat(o);i.val(o)}),y.on("change",".wpforms-panel-field-toggle",function(){var e=v(this);e.prop("disabled")||(e.prop("disabled",!0),k.toggleOptionsGroup(e))}),y.on("change",k.getPaymentsTogglesSelector(),function(e){var i=v(this),o=i.attr("id").replace(/wpforms-panel-field-|-enable|_one_time|_recurring/gi,""),t=v('.wpforms-panel-content-section-notifications [id*="-'+o+'-wrap"]');i.prop("checked")||v("#wpforms-panel-field-"+o+"-enable_one_time").prop("checked")||v("#wpforms-panel-field-"+o+"-enable_recurring").prop("checked")?v("#wpforms-panel-field-settings-disable_entries").prop("checked")?(v.confirm({title:wpforms_builder.heads_up,content:wpforms_builder.payments_entries_off,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}}),i.prop("checked",!1)):t.removeClass("wpforms-hidden"):(t.addClass("wpforms-hidden"),t.find('input[id*="-'+o+'"]').prop("checked",!1))}),y.on("change","#wpforms-panel-field-settings-disable_entries",function(e){var i=v(this);v("#wpforms-panel-field-settings-store_spam_entries-wrap").toggleClass("wpforms-hidden",i.prop("checked")),i.prop("checked")&&(k.isPaymentsEnabled()?(v.confirm({title:wpforms_builder.heads_up,content:wpforms_builder.payments_on_entries_off,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}}),i.prop("checked",!1)):v.alert({title:wpforms_builder.heads_up,content:wpforms_builder.disable_entries,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}}))}),y.on("click",".wpforms-image-upload-add",function(e){e.preventDefault();const o=v(this),t=o.parent(),r=wpf.initMediaLibrary({title:wpforms_builder.upload_image_title,extensions:wpforms_builder.upload_image_extensions,extensionsError:wpforms_builder.upload_image_extensions_error,buttonText:wpforms_builder.upload_image_button});r.on("select",function(){var e=r.state().get("selection").first().toJSON(),i=t.find(".preview");t.find(".source").val(e.url),i.empty(),i.prepend(''),"hide"===o.data("after-upload")&&o.hide(),y.trigger("wpformsImageUploadAdd",[o,t])}).on("close",function(){r.off("library:selection:add")}),r.open()}),y.on("click",".wpforms-image-upload-remove",function(e){e.preventDefault();e=v(this).parent().parent();e.find(".preview").empty(),e.find(".wpforms-image-upload-add").show(),e.find(".source").val(""),y.trigger("wpformsImageUploadRemove",[v(this),e])}),y.on("blur",".wpforms-notification .wpforms-panel-field-text input",function(){k.validateEmailSmartTags(v(this))}),y.on("blur",".wpforms-notification .wpforms-panel-field-textarea textarea",function(){k.validateEmailSmartTags(v(this))}),y.on("focusout",".wpforms-notification .wpforms-panel-field.js-wpforms-from-email-validation input",k.validateFromEmail),y.on("wpformsPanelSectionSwitch",k.notificationsPanelSectionSwitch),y.on("click","#wpforms-builder-mobile-notice .wpforms-fullscreen-notice-button-primary, #wpforms-builder-mobile-notice .close",function(){l.location.href=wpforms_builder.exit_url}),y.on("click","#wpforms-builder-mobile-notice .wpforms-fullscreen-notice-button-secondary",function(){l.location.href=wpf.updateQueryString("force_desktop_view",1,l.location.href)}),v("#wpforms-builder-license-alert .close").on("click",function(){l.location.href=wpforms_builder.exit_url}),v("#wpforms-builder-license-alert .dismiss").on("click",function(e){e.preventDefault(),v("#wpforms-builder-license-alert").remove(),wpCookies.set("wpforms-builder-license-alert","true",3600)}),y.on("change","#wpforms-panel-field-settings-akismet.wpforms-akismet-disabled",function(e){const i=v(this),o=i.data("akismet-status");i.prop("checked")&&v.alert({title:wpforms_builder.heads_up,content:wpforms_builder[o],icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}},onClose(){i.prop("checked",!1)}})}),y.on("wpformsPanelSectionSwitch wpformsPanelSwitched",function(){wpf.reInitShowMoreChoices(v("#wpforms-panel-providers, #wpforms-panel-settings"))})},notificationsPanelSectionSwitch(e,i){"notifications"===i&&v(".wpforms-notification .wpforms-panel-field.js-wpforms-from-email-validation input").trigger("focusout")},isPaymentsEnabled(){let e=!1;return v(k.getPaymentsTogglesSelector()).each(function(){if(v(this).prop("checked"))return!(e=!0)}),e},getPaymentsTogglesSelector(){return`.wpforms-panel-content-section-payment-toggle-one-time input, .wpforms-panel-content-section-payment-toggle-recurring input, #wpforms-panel-field-stripe-enable, #wpforms-panel-field-paypal_standard-enable, #wpforms-panel-field-authorize_net-enable, #wpforms-panel-field-square-enable`},toggleOptionsGroup(e){var i=e.attr("name");let o="";function t(){e.prop("disabled",!1)}const r=v('.wpforms-panel-field-toggle-body[data-toggle="'+i+'"]');k.toggleProviderActiveIcon(e),0===r.length?t():(i=e.attr("type"),o="checkbox"!==i&&"radio"!==i||e.prop("checked")?e.val():"0",r.each(function(){var e=v(this);e.attr("data-toggle-value").toString()===o.toString()?e.slideDown("",t):e.slideUp("",t)}))},toggleProviderActiveIcon(e){var o=e.closest(".wpforms-panel-content-section").data("provider"),t=["wpforms-panel-field-"+o+"-enable-wrap","wpforms-panel-field-"+o+"-enable_one_time-wrap","wpforms-panel-field-"+o+"-enable_recurring-wrap"];if(o&&t.includes(e.attr("id"))){let i=!1;t.forEach(e=>{e=v("#"+e);e.length&&e.find("input").is(":checked")&&(i=!0)}),v(`.wpforms-panel-sidebar-section[data-section=${o}]`).find(".fa-check-circle-o").toggleClass("wpforms-hidden",!i)}},toggleAllOptionGroups(e){(e=e||y||v("#wpforms-builder")||v("body"))&&e.find(".wpforms-panel-field-toggle").each(function(){var e=v(this);e.prop("disabled",!0),k.toggleOptionsGroup(e)})},toggleUnfoldableGroup(e){e.preventDefault();const i=v(e.target),o=i.closest(".wpforms-panel-fields-group"),t=o.find(".wpforms-panel-fields-group-inner"),r="wpforms_fields_group_"+o.data("group");o.hasClass("opened")?(wpCookies.remove(r),t.stop().slideUp(150,function(){o.removeClass("opened")})):(wpCookies.set(r,"true",2592e3),o.addClass("opened"),t.stop().slideDown(150))},hideFieldHelper(e){e.preventDefault(),e.stopPropagation();e=v(".wpforms-field-helper");wpCookies.set("wpforms_field_helper_hide","true",2592e3),e.hide()},smartTagToggle(e){e.preventDefault(),C.$focusOutTarget=null;var e=v(this),i=e.closest(".wpforms-panel-field,.wpforms-field-option-row");i.hasClass("smart-tags-toggling")||(i.addClass("smart-tags-toggling"),e.hasClass("smart-tag-showing")?k.removeSmartTagsList(e):k.insertSmartTagsList(e))},removeSmartTagsList(e){const i=e.closest(".wpforms-panel-field,.wpforms-field-option-row"),o=i.find(".smart-tags-list-display");e.find("span").text(wpforms_builder.smart_tags_show),o.slideUp("",function(){o.remove(),e.removeClass("smart-tag-showing"),i.removeClass("smart-tags-toggling")})},insertSmartTagsList(e){const i=e.closest(".wpforms-panel-field,.wpforms-field-option-row");let o=e.closest("label"),t=!0;o.length||(o=i.find("label"),t=!1);var r=k.getSmartTagsList(e,-1!==o.attr("for").indexOf("wpforms-field-option-"));(t?o:e).after(r),e.find("span").text(wpforms_builder.smart_tags_hide),i.find(".smart-tags-list-display").slideDown("",function(){e.addClass("smart-tag-showing"),i.removeClass("smart-tags-toggling")})},getSmartTagsList(e,i){var o='
      ';return(o+=k.getSmartTagsListFieldsElements(e))+k.getSmartTagsListOtherElements(e,i)+"
    "},getSmartTagsListFieldsElements(e){var i=e.data("type");if(!["fields","all"].includes(i))return"";var o=k.getSmartTagsFields(e);if(!o)return'
  • '+wpforms_builder.fields_unavailable+"
  • ";let t="";t+='
  • '+wpforms_builder.fields_available+"
  • ";for(const r in o)t+=k.getSmartTagsListFieldsElement(o[r]);return t},getSmartTagsFields(e){var i=e.data("fields"),e=e.data("allow-repeated-fields"),i=i?i.split(","):void 0;return wpf.getFields(i,!0,e)},getSmartTagsListFieldsElement(o){const t=o.label?wpf.encodeHTMLEntities(wpf.sanitizeHTML(o.label)):wpforms_builder.field+" #"+o.id;let r=`
  • ${t}
  • `;var e=o.additional||[];return 1{var i=e.charAt(0).toUpperCase()+e.slice(1).replace(/(\D)(\d)/g,"$1 $2");r+=`
  • ${t} – ${i}
  • `}),r},getSmartTagsListOtherElements(e,i){var o=e.data("type");let t;if("other"!==o&&"all"!==o)return"";t='
  • '+wpforms_builder.other+"
  • ";for(const r in wpforms_builder.smart_tags)i&&wpforms_builder.smart_tags_disabled_for_fields.includes(r)||"confirmations"===e.data("location")&&wpforms_builder.smart_tags_disabled_for_confirmations.includes(r)||(t+='
  • "+wpforms_builder.smart_tags[r]+"
  • ");return t},smartTagInsert(e){e.preventDefault();const i=v(this),o=i.closest(".smart-tags-list-display"),t=o.closest(".wpforms-panel-field,.wpforms-field-option-row"),r=t.find(".toggle-smart-tag-display"),s=t.find("input[type=text], textarea"),n=i.data("meta"),l=i.data("additional")?"|"+i.data("additional"):"",a=i.data("type");let d="field"===a?'{field_id="'+n+l+'"}':"{"+n+"}",p;"undefined"!=typeof tinyMCE&&(p=tinyMCE.get(s.prop("id")))&&!p.hasFocus()&&p.focus(!0),p&&!p.isHidden()?p.insertContent(d):(s.insertAtCaret(" "+d+" "),s.val(s.val().trim().replace(" "," ")),s.trigger("focus").trigger("input")),o.slideUp("",function(){o.remove()}),r.find("span").text(wpforms_builder.smart_tags_show),t.find(".toggle-smart-tag-display").removeClass("smart-tag-showing")},fieldMapTableDeleteRow(e,i){var i=v(i),o=i.closest("tr"),i=i.closest("table"),t=o.closest(".wpforms-builder-settings-block");"1"",{value:"",text:r})),l&&!v.isEmptyObject(l))for(const n in l){let e="";l[n]&&(e=void 0!==l[n].label&&""!==l[n].label.toString().trim()?wpf.sanitizeHTML(l[n].label.toString().trim()):wpforms_builder.field+" #"+n,0<=v.inArray(l[n].type,t)||0<=v.inArray("all-fields",t))&&o.append(v("' ); } if ( pee.indexOf( '' ) > -1 ) { // no P/BR around param and embed. pee = pee.replace( /(]*>)\s*/gmi, '$1' ); pee = pee.replace( /(?=(\s*))\2<\/object>/gmi, '' ); pee = pee.replace( /(?=(\s*))\2(<\/?(?:param|embed)[^>]*>)((?=(\s*))\2)/gmi, '$1' ); } /* eslint-disable no-useless-escape */ if ( pee.indexOf( ' -1 || pee.indexOf( ' -1 ) { // no P/BR around source and track. pee = pee.replace( /([<\[](?:audio|video)[^>\]]*[>\]])\s*/gmi, '$1' ); pee = pee.replace( /(?=(\s*))\2([<\[]\/(?:audio|video)[>\]])/gmi, '$1' ); pee = pee.replace( /(?=(\s*))\2(<(?:source|track)[^>]*>)(?=(\s*))\2/gmi, '$1' ); } pee = pee.replace( /\n\n+/gmi, '\n\n' ); // take care of duplicates. // make paragraphs, including one at the end. const pees = pee.split( /\n\s*\n/ ); pee = ''; pees.forEach( function( tinkle ) { pee += '

    ' + tinkle.replace( /^(?:\s+|\s+)$/g, '' ) + '

    \n'; } ); pee = pee.replace( /

    \s*<\/p>/gmi, '' ); // Under certain strange conditions, it could create a P of entire whitespace. pee = pee.replace( /

    ([^<]+)<\/(div|address|form)>/gmi, '

    $1

    ' ); pee = pee.replace( new RegExp( '

    \s*(]*>)\s*

    ', 'gmi' ), '$1', pee ); // don't pee all over a tag. pee = pee.replace( /

    (/gmi, '$1' ); // problem with nested lists. pee = pee.replace( /

    ]*)>/gmi, '

    ' ); pee = pee.replace( /<\/blockquote><\/p>/gmi, '

    ' ); pee = pee.replace( new RegExp( '

    \s*(]*>)', 'gmi' ), '$1' ); pee = pee.replace( new RegExp( '(]*>)\s*

    ', 'gmi' ), '$1' ); if ( br ) { pee = pee.replace( /<(script|style)(?:.|\n)*?<\/\\1>/gmi, _autopNewlinePreservationHelper ); // /s modifier from php PCRE regexp replaced with (?:.|\n). pee = pee.replace( /(
    )?((?=(\s*))\2)\n/gmi, '
    \n' ); // optionally make line breaks. pee = pee.replace( '', '\n' ); } pee = pee.replace( new RegExp( '(]*>)\s*
    ', 'gmi' ), '$1' ); pee = pee.replace( /
    (\s*<\/?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)/gmi, '$1' ); pee = pee.replace( /\n<\/p>$/gmi, '

    ' ); /* eslint-enable */ if ( Object.keys( preTags ).length ) { pee = pee.replace( new RegExp( Object.keys( preTags ).join( '|' ), 'gi' ), function( matched ) { return preTags[ matched ]; } ); } return pee; }, /** * Init Media Library. * * @since 1.8.6 * * @param {Object} args List of arguments. * * @return {wp.media.view.MediaFrame} A media workflow. */ initMediaLibrary( args ) { const mediaFrame = wp.media.frames.wpforms_media_frame = wp.media( { className: 'media-frame wpforms-media-frame', multiple: false, title: args.title, library: { type: args.extensions }, button: { text: args.buttonText, }, } ); mediaFrame.on( 'uploader:ready', function() { const accept = args.extensions.join( ',' ); jQuery( '.wpforms-media-frame .moxie-shim-html5 input[type="file"]' ) .attr( 'accept', accept ); } ).on( 'library:selection:add', function() { const attachment = mediaFrame.state().get( 'selection' ).first().toJSON(); if ( ! args.extensions.includes( attachment.file.type ) ) { // eslint-disable-next-line no-alert alert( args.extensionsError ); mediaFrame.state().get( 'selection' ).reset(); } } ); return mediaFrame; }, /** * Determine whether an element is visible in the viewport. * * @since 1.8.8 * * @param {jQuery} $element DOM element. * * @return {boolean} true if an element is visible in the viewport. */ isInViewport( $element ) { const rect = $element[ 0 ].getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= ( window.innerHeight || document.documentElement.clientHeight ) && rect.right <= ( window.innerWidth || document.documentElement.clientWidth ) ); }, }; wpf.init(); admin/share/admin-utils.min.js000064400000033143151716470030012314 0ustar00const wpf={cachedFields:{},savedState:!1,initialSave:!0,orders:{fields:[],choices:{}},init(){wpf.bindUIActions(),wpf.initRadioGroupForCheckboxes(),jQuery(wpf.ready)},ready(){wpf.savedState=wpf.getFormState("#wpforms-builder-form"),wpf.setFieldOrders(),wpf.setChoicesOrders()},bindUIActions(){jQuery(document).on("wpformsFieldAdd",wpf.setFieldOrders).on("wpformsFieldDuplicated",wpf.setFieldOrders).on("wpformsFieldDelete",wpf.setFieldOrders).on("wpformsFieldMove",wpf.setFieldOrders).on("wpformsFieldAdd",wpf.setChoicesOrders).on("wpformsFieldChoiceAdd",wpf.setChoicesOrders).on("wpformsFieldChoiceDelete",wpf.setChoicesOrders).on("wpformsFieldChoiceMove",wpf.setChoicesOrders).on("wpformsFieldAdd",wpf.fieldUpdate).on("wpformsFieldDelete",wpf.fieldUpdate).on("wpformsFieldMove",wpf.fieldUpdate).on("wpformsFieldChoiceAdd",wpf.fieldUpdate).on("wpformsFieldChoiceDelete",wpf.fieldUpdate).on("wpformsFieldChoiceMove",wpf.fieldUpdate).on("wpformsFieldDynamicChoiceToggle",wpf.fieldUpdate).on("focusout",".wpforms-field-option-row-label input",wpf.fieldUpdate).on("focusout",".wpforms-field-option-row-choices input.label",wpf.fieldUpdate)},setFieldOrders(){wpf.orders.fields=[],jQuery(".wpforms-field-option").each(function(){wpf.orders.fields.push(jQuery(this).data("field-id"))})},setChoicesOrders(){wpf.orders.choices={},jQuery(".choices-list").each(function(){const e=jQuery(this).data("field-id");wpf.orders.choices["field_"+e]=[],jQuery(this).find("li").each(function(){wpf.orders.choices["field_"+e].push(jQuery(this).data("key"))})})},getChoicesOrder(e){const r=[];return jQuery("#wpforms-field-option-"+e).find(".choices-list li").each(function(){r.push(jQuery(this).data("key"))}),r},initMultipleSelectWithSearch(e){const r=jQuery(e.passedElement.element),t=jQuery(e.input.element);r.prop("multiple")&&(t.data("placeholder",t.attr("placeholder")),t.data("style",t.attr("style")),e.getValue(!0).length&&t.removeAttr("placeholder"),r.on("change",function(){e.getValue(!0).length?t.removeAttr("placeholder"):t.attr("placeholder",t.data("placeholder")).attr("style",t.data("style"))}))},showMoreButtonForChoices(e){var r,t;"select-one"!==jQuery(e).data("type")&&(r=jQuery(e).find(".choices__list--multiple .choices__item").first(),t=jQuery(e).find(".choices__list--multiple .choices__item").last(),jQuery(e).removeClass("choices__show-more"),0{e.find(".choices select").each(function(){var e=jQuery(this).data("choicesjs");wpf.showMoreButtonForChoices(e.containerOuter.element)})},100)},fieldUpdate(){var e=wpf.getFields();jQuery(document).trigger("wpformsFieldUpdate",[e]),wpf.debug("fieldUpdate triggered")},getFields(e=void 0,r=void 0,t=void 0,i=void 0){let o;if((r=r||!1)&&!jQuery.isEmptyObject(wpf.cachedFields))o=jQuery.extend({},wpf.cachedFields),wpf.debug("getFields triggered (cached)");else{var r=wpf.formObject("#wpforms-field-options"),n=["captcha","content","divider","entry-preview","html","internal-information","pagebreak","layout"];if(!(o=r.fields))return!1;for(const d in o)(!o[d].type||-1{Object.values(e?.fields??[]).forEach(e=>{o[e]&&(o[e].label+=" ("+o[d].label+")",o[e].isRepeater=!0)})}),delete o[d]);wpf.addAdditionalFields(o),wpf.cachedFields=jQuery.extend({},o),wpf.debug("getFields triggered")}if(!t)for(const a in o)o[a]?.isRepeater&&delete o[a];if(i)for(const p in i)delete o[p];if(e&&e.constructor===Array)for(const c in o)-1===jQuery.inArray(o[c].type,e)&&delete o[c];if(0===Object.keys(o).length)return!1;var s=[];for(const f in wpf.orders.fields){var l=wpf.orders.fields[f];o[l]&&s.push(o[l])}return Object.assign({},s)},addAdditionalFields(e){for(const t in e){var r;if(["name","date-time"].includes(e[t]?.type)&&(r=e[t].format)&&(e[t].additional=r.split("-")),"address"===e[t]?.type){const i=Object.keys(e[t]).filter(e=>e.includes("_placeholder"));i.forEach((e,r)=>{i[r]=e.replace("_placeholder","")}),e[t].additional=i}}return e},getField(e){e=wpf.formObject("#wpforms-field-option-"+e);return Object.keys(e).length?e.fields[Object.keys(e.fields)[0]]:{}},fieldOptionLoading(e,r=void 0){var e=jQuery(e),t=e.find("label");(r=void 0!==r)?(t.find(".wpforms-loading-spinner").remove(),t.find(".wpforms-help-tooltip").show(),e.find("input,select,textarea").prop("disabled",!1)):(t.append(''),t.find(".wpforms-help-tooltip").hide(),e.find("input,select,textarea").prop("disabled",!0))},getFormState(e){return jQuery(e).serialize()},removeArrayItem(r,t){let i=0;for(let e=0;e&]/gim,function(e){return"&#"+e.charCodeAt(0)+";"})},decodeAllowedHTMLEntities(e){"string"!=typeof e&&(e=e.toString());var r=wp.hooks.applyFilters("wpforms.allowedHTMLEntities",{"&":"&"," ":" "});for(const t in r)e=e.replaceAll(t,r[t]);return e},initRadioGroupForCheckboxes(){const n=jQuery;n(document).on("change",'input[type="checkbox"].wpforms-radio-group',function(){var r=n(this);if(r.prop("checked")){const t=r.data("radio-group"),i=n(".wpforms-radio-group-"+t),o=r.attr("id");let e;i.each(function(){(e=n(this)).attr("id")!==o&&e.prop("checked",!1)})}})},listPluck(e,r){return e.map(function(e){return void 0!==e?e[r]:e})},triggerEvent(e,r){return console.warn('WARNING! Function "wpf.triggerEvent( $element, eventName )" has been deprecated, please use the new "WPFormsUtils.triggerEvent( $element, eventName, args )" function instead!'),WPFormsUtils.triggerEvent(e,r)},wpautop(i,e=!0){const o=new Map;if("string"==typeof i||i instanceof String){if(""===i.trim())return"";-1<(i+="\n").indexOf("")).pop(),i="",r.forEach(function(e,r){var t=e.indexOf("",o[r]=e.substring(t)+"",i+=e.substring(0,t)+r)}),i+=t);var r="(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)",t=(i=(i=-1<(i=-1<(i=-1<(i=0===(i=(i=(i=(i=i.replace(/
    \s*
    /,"\n\n")).replace(new RegExp("(<"+r+"[^>]*>)","gmi"),"\n$1")).replace(new RegExp("()","gmi"),"$1\n\n")).replace(/\r\n|\r/,"\n")).indexOf("\n")?i.substring(1):i).indexOf("\s*/gim,""):i).indexOf("")?(i=(i=i.replace(/(]*>)\s*/gim,"$1")).replace(/(?=(\s*))\2<\/object>/gim,"")).replace(/(?=(\s*))\2(<\/?(?:param|embed)[^>]*>)((?=(\s*))\2)/gim,"$1"):i).indexOf("\]]*[>\]])\s*/gim,"$1")).replace(/(?=(\s*))\2([<\[]\/(?:audio|video)[>\]])/gim,"$1")).replace(/(?=(\s*))\2(<(?:source|track)[^>]*>)(?=(\s*))\2/gim,"$1"):i).replace(/\n\n+/gim,"\n\n")).split(/\n\s*\n/);i="",t.forEach(function(e){i+="

    "+e.replace(/^(?:\s+|\s+)$/g,"")+"

    \n"}),i=(i=(i=(i=(i=(i=(i=(i=i.replace(/

    \s*<\/p>/gim,"")).replace(/

    ([^<]+)<\/(div|address|form)>/gim,"

    $1

    ")).replace(new RegExp("

    s*(]*>)s*

    ","gmi"),"$1",i)).replace(/

    (/gim,"$1")).replace(/

    ]*)>/gim,"

    ")).replace(/<\/blockquote><\/p>/gim,"

    ")).replace(new RegExp("

    s*(]*>)","gmi"),"$1")).replace(new RegExp("(]*>)s*

    ","gmi"),"$1"),i=(i=(i=(i=e?(i=(i=i.replace(/<(script|style)(?:.|\n)*?<\/\\1>/gim,function(e){return e[0].replace("\n","")})).replace(/(
    )?((?=(\s*))\2)\n/gim,"
    \n")).replace("","\n"):i).replace(new RegExp("(]*>)s*
    ","gmi"),"$1")).replace(/
    (\s*<\/?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)/gim,"$1")).replace(/\n<\/p>$/gim,"

    "),Object.keys(o).length&&(i=i.replace(new RegExp(Object.keys(o).join("|"),"gi"),function(e){return o[e]}))}return i},initMediaLibrary(r){const t=wp.media.frames.wpforms_media_frame=wp.media({className:"media-frame wpforms-media-frame",multiple:!1,title:r.title,library:{type:r.extensions},button:{text:r.buttonText}});return t.on("uploader:ready",function(){var e=r.extensions.join(",");jQuery('.wpforms-media-frame .moxie-shim-html5 input[type="file"]').attr("accept",e)}).on("library:selection:add",function(){var e=t.state().get("selection").first().toJSON();r.extensions.includes(e.file.type)||(alert(r.extensionsError),t.state().get("selection").reset())}),t},isInViewport(e){e=e[0].getBoundingClientRect();return 0<=e.top&&0<=e.left&&e.bottom<=(window.innerHeight||document.documentElement.clientHeight)&&e.right<=(window.innerWidth||document.documentElement.clientWidth)}};wpf.init();admin/share/contrast-checker.js000064400000014471151716470030012546 0ustar00/* global define */ /* eslint-disable */ /** * This file contains a reusable, and dependency-free JavaScript class, * providing a contrast checker. This class allows you to assess the readability * of the given background and text colors against the WCAG 2.0 AA standard. * * Example Usage: * * // Create an instance of the plugin with custom settings. * const contrastChecker = new WPFormsColorContrastChecker({ * textColor: '#de8e8e', // Replace with your actual text color. * bgColor: '#ffffff', // Replace with your actual background color. * message: { * contrastPass: '', * contrastFail: 'Insufficient contrast. Please choose a darker text color or a lighter background color.', * }, * }); * * // Perform the contrast check. * const contrastFailed = contrastChecker.checkContrast(); * * // Display the result or handle the error, if any. * if (contrastFailed) { * console.error(contrastFailed); * } */ /* eslint-enable */ ( function( root, factory ) { const pluginName = 'WPFormsColorContrastChecker'; if ( typeof define === 'function' && define.amd ) { define( [], factory( pluginName ) ); } else if ( typeof exports === 'object' ) { module.exports = factory( pluginName ); } else { root[ pluginName ] = factory( pluginName ); } // eslint-disable-next-line max-lines-per-function }( this, function( pluginName ) { // eslint-disable-next-line strict 'use strict'; /** * Plugin Error Object. * * @since 1.8.6 * * @class PluginError * * @augments Error */ class PluginError extends Error { /** * Constructor. * * @since 1.8.6 * * @param {string} message The error message. */ constructor( message ) { super( message ); this.name = pluginName; } } /** * Log the error message. * This function can be replaced with a custom error logging logic. * * @since 1.8.6 * * @param {string} error The error message. */ function logError( error ) { // Custom error logging logic. // Display the error message in a specific format or send it to a logging service // eslint-disable-next-line no-console console.error( error ); } /** * Plugin Object. * * @since 1.8.6 * * @class Plugin */ class Plugin { // Default settings. static defaults = { textColor: '', bgColor: '', contrastThreshold: 4.5, // W3C recommended minimum contrast ratio for normal text message: { contrastPass: 'The contrast ratio between the text and background color is sufficient.', contrastFail: 'The contrast ratio between the text and background color is insufficient. Please choose a darker text color or a lighter background color.', }, }; /** * Constructor. * * @since 1.8.6 * * @param {Object} args The argument object. */ constructor( args ) { // Merge the default settings with the provided settings. this.args = Object.assign( {}, Plugin.defaults, args ); } /** * Convert hex color code to an RGB array. * * @since 1.8.6 * * @param {string} hexColor The hex color code. * * @return {number[]|null} The RGB array or null if the conversion failed. */ hexToRgb( hexColor ) { const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec( hexColor ); if ( shorthandRegex.test( hexColor ) ) { return result ? [ parseInt( result[ 1 ], 16 ) * 17, parseInt( result[ 2 ], 16 ) * 17, parseInt( result[ 3 ], 16 ) * 17, ] : null; } return result ? [ parseInt( result[ 1 ], 16 ), parseInt( result[ 2 ], 16 ), parseInt( result[ 3 ], 16 ), ] : null; } /** * Calculate relative luminance for a color. * * The calculated relative luminance is a value between 0 and 1, * where 0 represents black and 1 represents white. * * @since 1.8.6 * * @param {string} rgb The RGB color code. * * @return {number} The relative luminance. */ calculateRelativeLuminance( rgb ) { for ( let i = 0; i < rgb.length; i++ ) { rgb[ i ] /= 255; rgb[ i ] = rgb[ i ] <= 0.03928 ? rgb[ i ] / 12.92 : Math.pow( ( rgb[ i ] + 0.055 ) / 1.055, 2.4 ); } // As Stated in WCAG the relative luminance of a color is defined as: // L = 0.2126 * R + 0.7152 * G + 0.0722 * B // where R, G and B are the color values normalized to the range [0, 1]. // @see https://www.w3.org/WAI/GL/wiki/Relative_luminance // eslint-disable-next-line no-mixed-operators return 0.2126 * rgb[ 0 ] + 0.7152 * rgb[ 1 ] + 0.0722 * rgb[ 2 ]; } /** * Get the contrast ratio between two colors. * * @since 1.8.6 * * @return {number|null} The contrast ratio or an error if the calculation failed. */ getContrastRatio() { try { const rgbText = this.hexToRgb( this.args.textColor ); const rgbBg = this.hexToRgb( this.args.bgColor ); // Check for invalid color format if ( ! rgbText || ! rgbBg ) { throw new PluginError( 'Invalid color format. Provide valid hex color codes.' ); } const [ l1, l2 ] = [ this.calculateRelativeLuminance( rgbText ), this.calculateRelativeLuminance( rgbBg ) ]; // The purpose of adding 0.05 to both the maximum and minimum relative luminance // is to ensure that even if one of the luminance values is zero (which would cause division by zero), // the result won't be infinite. This kind of adjustment is common in contrast ratio calculations // to handle extreme cases and avoid mathematical errors. return ( Math.max( l1, l2 ) + 0.05 ) / ( Math.min( l1, l2 ) + 0.05 ); } catch ( error ) { logError( error.message ); return null; } } /** * Check the contrast and provide a warning if it's below the threshold. * * @since 1.8.6 * * @return {string|null} The contrast check result or boolean false if the check failed. */ checkContrast() { try { const contrastRatio = this.getContrastRatio(); // Return early if invalid color format if ( contrastRatio === null ) { throw new PluginError( 'Invalid contrast ratio. Provide valid contrast ratio between two colors.' ); } // Warn if the contrast is below the threshold. if ( contrastRatio < this.args.contrastThreshold ) { return this.args.message.contrastFail; } return this.args.message.contrastPass; } catch ( error ) { logError( error.message ); return null; } } } return Plugin; } ) ); admin/share/contrast-checker.min.js000064400000003262151716470030013324 0ustar00!function(t,e){var r="WPFormsColorContrastChecker";"function"==typeof define&&define.amd?define([],e(r)):"object"==typeof exports?module.exports=e(r):t[r]=e(r)}(this,function(e){"use strict";class o extends Error{constructor(t){super(t),this.name=e}}function s(t){console.error(t)}return class r{static defaults={textColor:"",bgColor:"",contrastThreshold:4.5,message:{contrastPass:"The contrast ratio between the text and background color is sufficient.",contrastFail:"The contrast ratio between the text and background color is insufficient. Please choose a darker text color or a lighter background color."}};constructor(t){this.args=Object.assign({},r.defaults,t)}hexToRgb(t){var e=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return/^#?([a-f\d])([a-f\d])([a-f\d])$/i.test(t)?e?[17*parseInt(e[1],16),17*parseInt(e[2],16),17*parseInt(e[3],16)]:null:e?[parseInt(e[1],16),parseInt(e[2],16),parseInt(e[3],16)]:null}calculateRelativeLuminance(e){for(let t=0;t
    ' ); el.$tableContainer = el.$table.parent(); } // Add specific classes to the page container. el.$page.addClass( 'wpforms-list-table-ext-page' ); }, /** * Init pagination. * * @since 1.9.3 */ initPagination() { // Prevent the error messages in console. htmx.config.historyCacheSize = 2; const perPage = $( '#pagination_per_page, #wpforms_entries_per_page' ).val(); // Do not proceed if the perPage value is too high. // The HTMX pagination will be disabled in this case to avoid console errors coused by the large size of the page HTML. if ( perPage > 200 ) { return; } $( '.tablenav-pages .pagination-links a' ).each( function() { const $link = $( this ); const url = $link.attr( 'href' ); $link .attr( { 'hx-get': url, 'hx-target': '.wpforms-admin-content', 'hx-swap': 'outerHTML', 'hx-select': '.wpforms-admin-content', 'hx-replace-url': 'true', } ); htmx.process( $link[ 0 ] ); } ); }, /** * Click pagination button event handler. * * @since 1.9.3 */ clickPaginationButton() { el.$body.addClass( 'wpforms-loading' ); }, /** * The search term keydown event handler. * * @since 1.9.3 * * @param {Event} e Event. */ searchTermKeydown( e ) { if ( e.keyCode === 13 ) { $( '#current-page-selector' ).val( 1 ); } }, /** * The `htmx:beforeSwap` event handler. * * @since 1.9.3 */ htmxBeforeSwap() { el.$cog.detach(); }, /** * The `htmx:afterSettle` event handler. * * @since 1.9.3 */ htmxAfterSettle() { app.initElements(); app.initMultiSelect(); app.prepareTableFootColumns(); app.initTableSortableColumns(); app.initTableScrollColumns(); el.$tableScroll?.on( 'scroll', app.tableScroll ); app.windowResize(); app.initPagination(); app.initMobileRowExpander(); window.WPFormsForms?.Overview.htmxAfterSettle(); window.WPFormsPagesEntries?.htmxAfterSettle(); el.$body.removeClass( 'wpforms-loading' ); }, /** * Init mobile view row expander. * * @since 1.9.3 */ initMobileRowExpander() { $( 'tbody' ).on( 'click', '.toggle-row', function() { $( this ).closest( 'tr' ).toggleClass( 'is-expanded' ); } ); }, /** * Prepare table footer columns. Their IDs should match the IDs of the header columns. * * @since 1.8.6 */ prepareTableFootColumns() { el.$table.find( 'thead tr .manage-column' ).each( function() { const columnId = $( this ).attr( 'id' ); el.$table.find( 'tfoot tr .column-' + columnId ).attr( 'id', columnId + '-foot' ); } ); // Disable sorting of the cog column. el.$table.find( '.manage-column.column-cog' ) .addClass( 'wpforms-table-cell-sticky' ); }, /** * Initialize table columns sortable container. * * @since 1.8.6 */ initTableSortableColumns() { // eslint-disable-line max-lines-per-function let $columnCells, columnId; el.$table.find( 'thead tr, tfoot tr' ).each( function() { // eslint-disable-line max-lines-per-function const $sortable = $( this ); $sortable.sortable( { items: '> th:not(:first-child):not(.wpforms-table-cell-sticky)', connectWith: '', delay: 100, opacity: 0.75, cursor: 'move', cancel: '.wpforms-table-column-not-draggable', placeholder: 'wpforms-table-column-drag-placeholder', appendTo: el.$page, zindex: 10000, tolerance: 'intersect', distance: 1, helper( e, origin ) { const $el = $( origin ), $helper = $el.clone(), width = $el.outerWidth(); return $helper.css( 'width', width + 'px' ); }, start( e, ui ) { ui.helper.addClass( 'wpforms-table-column-drag-helper' ); // Add a specific class to the helper container. ui.item.addClass( 'wpforms-table-column-dragged-out' ).css( 'display', '' ); // Disable global scrolling. el.$wpcontent.addClass( 'wpforms-no-scroll' ); columnId = ui.item.attr( 'id' ).replace( '-foot', '' ); }, stop( e, ui ) { // Remove specific classes from the helper. ui.item .removeClass( 'wpforms-table-column-drag-helper' ) .removeClass( 'wpforms-table-column-dragged-out' ); // Remove previously added vertical placeholder class from all columns. el.$table.find( 'thead tr > *, tfoot tr > *' ).removeClass( 'wpforms-table-column-drag-placeholder-prev' ); // Enable global scrolling. el.$wpcontent.removeClass( 'wpforms-no-scroll' ); const prevColumnId = ui.item.prev().attr( 'id' ).replace( '-foot', '' ), $rows = el.$table.find( 'tbody tr:not(.wpforms-hidden)' ), prevSelector = prevColumnId !== 'cb' ? '.column-' + prevColumnId : '.check-column'; // Move column cells. $columnCells = $rows.find( 'td.column-' + columnId ).detach(); for ( let i = 0; i < $columnCells.length; i++ ) { $rows.eq( i ).find( prevSelector ).after( $columnCells.eq( i ) ); } // Move opposite column header. const oppositeColumnsSelector = ui.item.closest( 'thead' ).length > 0 ? 'tfoot' : 'thead', $oppositeColumn = el.$table.find( oppositeColumnsSelector + ' tr .column-' + columnId ).detach(); el.$table.find( oppositeColumnsSelector + ' tr ' + prevSelector ).after( $oppositeColumn ); app.updateMenuColumnsOrder(); }, change( e, ui ) { // Remove previously added vertical placeholder class from all columns. el.$table.find( 'thead tr > *, tfoot tr > *' ).removeClass( 'wpforms-table-column-drag-placeholder-prev' ); // Add the vertical placeholder class to the previous column. ui.placeholder.prev().addClass( 'wpforms-table-column-drag-placeholder-prev' ); }, update() { app.saveColumnsOrder(); }, } ); } ); }, /** * Initialize table scroll sticky columns. * * @since 1.8.6 */ initTableScrollColumns() { // Init table horizontal scrolling only on the Entries page. if ( ! el.$page.is( '#wpforms-entries-list' ) ) { return; } el.$tableScroll = el.$tableContainer; // The Entries page has own table container, add the class. el.$tableScroll.addClass( 'wpforms-table-scroll' ); // Detect the Windows OS platform. el.$tableScroll.toggleClass( 'wpforms-scrollbar', app.isCustomScrollbarNeeded() ); // Add specific class to the sticky columns. el.$table.find( '.check-column, .column-indicators' ) .addClass( 'wpforms-table-cell-sticky' ) .addClass( 'left' ); el.$table.find( '.column-actions' ) .addClass( 'wpforms-table-cell-sticky' ) .addClass( 'right' ); }, /** * Table scroll event. * * @since 1.8.6 */ tableScroll() { if ( ! el.$tableScroll?.length ) { return; } const width = el.$tableScroll.outerWidth(), scrollLeft = Math.abs( el.$tableScroll.get( 0 ).scrollLeft ), scrollWidth = el.$tableScroll.get( 0 ).scrollWidth; // Conditionally Add shadow to the sticky columns. el.$tableScroll .find( '.wpforms-table-cell-sticky.left' ) .toggleClass( 'shadow', scrollLeft > 1 ); // 1px is fix for the RTL mode. el.$tableScroll .find( '.wpforms-table-cell-sticky.right' ) .toggleClass( 'shadow', scrollWidth - width >= scrollLeft ); }, /** * Window resize event. * * @since 1.8.6 */ windowResize() { // Disable dragging on mobiles. el.$table.find( 'thead th, tfoot th' ).toggleClass( 'wpforms-table-column-not-draggable', window.innerWidth <= 782 ); app.closeMenu(); app.windowResizeToggleColumns(); app.tableScroll(); }, /** * Toggle columns visibility for certain window sizes. * * @since 1.8.6 */ windowResizeToggleColumns() { // Proceed only on the Forms Overview page. if ( ! el.$page.is( '#wpforms-overview' ) ) { return; } const $visibleColumns = el.$table.find( 'thead tr th:visible' ); const $columnTags = el.$table.find( '.column-tags' ); // For browser window with the width between 960px and 1280px. if ( window.innerWidth > 960 && window.innerWidth <= 1280 ) { $columnTags.toggleClass( 'wpforms-hidden', $visibleColumns.length > 4 ); } else { $columnTags.removeClass( 'wpforms-hidden' ); } // Synchronize menu items visibility. el.$menu.find( 'label' ).removeClass( 'wpforms-hidden' ); el.$table.find( 'thead tr th:not(:visible)' ).each( function() { const $column = $( this ); el.$menu .find( `input[value="${ $column.attr( 'id' ) }"]` ) .closest( 'label' ) .addClass( 'wpforms-hidden' ); } ); }, /** * Show or hide no results text. * * @since 1.8.6 */ maybeShowNoResults() { [ 'fields', 'meta' ].forEach( ( section ) => { const labels = el.$menu.find( '.wpforms-multiselect-checkbox-optgroup-' + section ) .nextUntil( '.wpforms-multiselect-checkbox-optgroup' ) .filter( 'label' ); const hiddenLabels = labels.filter( function() { return $( this ).is( ':hidden' ); } ); el.$menu.find( '.wpforms-multiselect-checkbox-no-results-' + section ) .toggleClass( 'wpforms-hidden', labels.length !== hiddenLabels.length ); } ); }, /** * Close the columns' selector menu. * * @since 1.8.6 */ closeMenu() { if ( ! el.$cog.hasClass( 'active' ) ) { return; } el.$cog.removeClass( 'active' ); el.$menu.find( '.wpforms-multiselect-checkbox-list' ).removeClass( 'open' ); // Flush the search input. el.$searchInput.val( '' ); el.$searchInput[ 0 ]?.dispatchEvent( new Event( 'input' ) ); }, /** * Get columns order. * * @since 1.8.6 * * @return {Array} Columns order. */ getColumnsOrder() { const $row = el.$table.find( 'thead tr' ); const columns = []; $row.find( 'th' ).each( function() { columns.push( $( this ).attr( 'id' ) ); } ); return columns; }, /** * Get menu columns order. * * @since 1.8.6 * * @return {Array} Columns order. */ getMenuColumnsOrder() { let columnsOrder = app.getColumnsOrder(); const columnsChecked = []; const columns = []; el.$menu.find( `input:checked` ).each( function() { columnsChecked.push( $( this ).val() ); } ); // Convert DOM element IDs to column IDs. columnsOrder = columnsOrder.map( function( column ) { return app.convertColumnId( column ); } ); // Add checked columns in the same order as in the table. for ( let i = 0; i < columnsOrder.length; i++ ) { const column = columnsOrder[ i ]; if ( columnsChecked.includes( column ) ) { columns.push( column ); columnsChecked.splice( columnsChecked.indexOf( column ), 1 ); } } // Add the rest of the checked columns. return columns.concat( columnsChecked ); }, /** * Save columns order. * * @since 1.8.6 */ saveColumnsOrder() { const data = { nonce: wpforms_admin.nonce, action: el.$menu.find( '[name="action"]' ).val(), form_id: el.$menu.find( '[name="form_id"]' ).val(), // eslint-disable-line camelcase columns: app.getColumnsOrder(), }; // AJAX request to save the columns order. $.post( wpforms_admin.ajax_url, data ) .done( function( response ) { if ( ! response.success ) { app.displayErrorModal( response.data || wpforms_admin.unknown_error ); } } ) .fail( function() { app.displayErrorModal( wpforms_admin.server_error ); } ); }, /** * Display modal window with an error message. * * @since 1.8.6 * * @param {string} content Modal content. */ displayErrorModal( content ) { $.alert( { title : wpforms_admin.uh_oh, content, icon : 'fa fa-exclamation-circle', type : 'red', buttons: { cancel: { text : wpforms_admin.close, btnClass: 'btn-confirm', keys : [ 'enter' ], }, }, } ); }, /** * Update menu columns order. * * @since 1.8.6 */ updateMenuColumnsOrder() { // eslint-disable-line complexity let columnsOrder = app.getColumnsOrder(); const $groups = el.$menu.find( '.wpforms-multiselect-checkbox-optgroup' ); const $itemsCont = el.$menu.find( '.wpforms-multiselect-checkbox-items' ); const $items = $itemsCont.find( 'label' ); const itemsByGroup = [ 0 ]; // If there are no groups, add the items to the first group. itemsByGroup[ 0 ] = $items; // If there are groups, split the items by groups. if ( $groups.length ) { $groups.each( function( i ) { itemsByGroup[ i ] = $( this ).nextUntil( '.wpforms-multiselect-checkbox-optgroup' ); } ); } // Convert DOM element IDs to column IDs. columnsOrder = columnsOrder.map( function( column ) { return app.convertColumnId( column ); } ); // Rebuild the menu items order. for ( let g = 0; g < itemsByGroup.length; g++ ) { itemsByGroup[ g ] = itemsByGroup[ g ].filter( function() { return $( this ).find( 'input:checked' ).length > 0; } ); itemsByGroup[ g ].detach(); const $group = $groups.eq( g ); // Add the items in the same order as in the table. // It is necessary to process it in reverse mode to reproduce the columns order. for ( let i = columnsOrder.length - 1; i >= 0; i-- ) { const column = columnsOrder[ i ]; const $item = itemsByGroup[ g ].filter( function() { return $( this ).find( `[value="${ column }"]` ).length > 0; } ); if ( ! $item.length ) { continue; } if ( $group.length ) { $group.after( $item ); } else { $itemsCont.prepend( $item ); } } } }, /** * Convert column Id. * * @since 1.8.6 * * @param {string|number} columnId Column ID. * * @return {string} Converted column ID. */ convertColumnId( columnId ) { let id = columnId.replace( 'wpforms_field_', '' ); id = id.replace( '-foot', '' ); id = id === 'entry_id' ? '-1' : id; id = id === 'notes_count' ? '-2' : id; return id; }, /** * Initialize wpforms-multiselect-js on select elements. * * @since 1.8.6 */ initMultiSelect() { if ( ! el.$cog.length ) { return; } el.$menu.find( '.wpforms-list-table-ext-edit-columns-select' ).each( function() { const $select = $( this ); const isLongList = $select.find( 'option' ).length > 10; const isEntriesPage = el.$page.is( '#wpforms-entries-list' ); const showSearch = isEntriesPage && isLongList; const $selectWrapper = $select.parent( '.wpforms-multiselect-checkbox-dropdown' ); // If the multiselect is already initialized, skip. if ( $selectWrapper.length ) { return; } const multiSelectColumns = new window.WPFormsMultiSelectCheckbox( this, { showMask: true, showSearch, customOpener: el.$cog.get( 0 ), } ); // Initialize the multiselect. multiSelectColumns.init(); const $wrapper = $select.next( '.wpforms-multiselect-checkbox-wrapper' ); const $list = $wrapper.find( '.wpforms-multiselect-checkbox-list' ); app.appendNoResultsText( $list ); if ( ! showSearch ) { $wrapper.find( '.wpforms-multiselect-checkbox-items' ).addClass( 'wpforms-multiselect-checkbox-items-no-search' ); } $list.append( '' ); app.updateMenuColumnsOrder(); } ); el.$searchInput = $( '#wpforms-list-table-ext-edit-columns-select-container .wpforms-multiselect-checkbox-search' ); el.$menu.removeClass( 'wpforms-hidden' ); }, /** * Append no results text to the multiselect list. * * @since 1.8.6 * * @param {jQuery} $list Multiselect list. */ appendNoResultsText( $list ) { $list.find( '.wpforms-multiselect-checkbox-optgroup' ).each( function( i ) { const appendix = i === 0 ? 'fields' : 'meta'; const noResultsText = i === 0 ? wpforms_admin.column_selector_no_fields : wpforms_admin.column_selector_no_meta; $( this ) .addClass( 'wpforms-multiselect-checkbox-optgroup-' + appendix ) .after( `${ noResultsText }` ); } ); }, /** * Add cog icon to the table header. * * @since 1.8.6 * * @return {jQuery} Cog icon object. */ initCogIcon() { const $lastColumnHeader = el.$table.find( 'thead th:not(.hidden):last' ); if ( ! $lastColumnHeader.length ) { return $(); } if ( el.$cog ) { $lastColumnHeader.append( el.$cog ); return el.$cog; } const cogId = selectors.cogIcon.replace( '#', '' ); const $cog = $( `` ); $lastColumnHeader.append( $cog ); return $cog; }, /* * Click on the cog icon. * * @since 1.8.6 * * @param {object} event Event object. */ onClickCog( event ) { event.preventDefault(); }, /* * Save changes. * * @since 1.8.6 * * @param {object} event Event object. */ onSaveChanges( event ) { event.preventDefault(); const data = { nonce: wpforms_admin.nonce, action: el.$menu.find( 'input[name="action"]' ).val(), form_id: el.$menu.find( 'input[name="form_id"]' ).val(), // eslint-disable-line camelcase columns: app.getMenuColumnsOrder(), }; app.closeMenu(); $.post( wpforms_admin.ajax_url, data ) .done( function( response ) { if ( ! response.success ) { app.displayErrorModal( response.data || wpforms_admin.unknown_error ); return; } window.location.reload(); } ) .fail( function() { app.displayErrorModal( wpforms_admin.server_error ); } ); }, /** * Toggle multiselect columns menu. * * @since 1.8.6 * * @param {Object} event Event object. */ onMenuToggle( event ) { $( selectors.cogIcon ).toggleClass( 'active', event.detail.isOpen ); // Hide no results messages. el.$menu.find( '.wpforms-multiselect-checkbox-no-results' ).addClass( 'wpforms-hidden' ); app.positionMultiselectColumnsMenu(); }, /** * Position the multiselect columns menu just under the cog icon. * * @since 1.8.6 */ positionMultiselectColumnsMenu() { if ( ! el.$cog.length ) { return; } el.$menu.css( { top: el.$cog.offset().top - $( '#wpbody-content' ).offset().top + el.$cog.outerHeight() + 6, } ); }, /** * Detect if the custom styled scrollbar is needed. * * @since 1.8.6 * * @return {boolean} True when needed. */ isCustomScrollbarNeeded() { const ua = navigator.userAgent; return ( ua.includes( 'Windows' ) || ua.includes( 'Linux' ) ) && ( ua.includes( 'Chrome' ) || ua.includes( 'Firefox' ) ); }, }; return app; }( document, window, jQuery ) ); // Initialize. WPFormsAdminListTableExt.init(); admin/share/list-table-ext.min.js000064400000025105151716470030012723 0ustar00var WPFormsAdminListTableExt=window.WPFormsAdminListTableExt||function(e,n,i){const t=["#wpforms-overview","#wpforms-entries-list"],o={cogIcon:"#wpforms-list-table-ext-edit-columns-cog",submitButton:"#wpforms-list-table-ext-edit-columns-select-submit"},c={},d={init(){d.initElements(),c.$doc.on("wpformsReady",d.initMultiSelect),i(d.ready)},ready(){d.initPagination(),d.prepareTableFootColumns(),d.initTableScrollColumns(),d.initTableSortableColumns(),d.events(),d.windowResize()},events(){c.$doc.on("click",o.cogIcon,d.onClickCog).on("wpforms_multiselect_checkbox_list_toggle",d.onMenuToggle).on("click",o.submitButton,d.onSaveChanges).on("click",".tablenav-pages a.button",d.clickPaginationButton).on("keydown","#wpforms-overview-search-term",d.searchTermKeydown).on("htmx:beforeSwap",d.htmxBeforeSwap).on("htmx:afterSettle",d.htmxAfterSettle),c.$tableScroll?.on("scroll",d.tableScroll),i(n).on("resize",_.debounce(d.windowResize,100)),c.$searchInput?.on("input",_.debounce(d.maybeShowNoResults,310))},initElements(){c.$doc=i(e),c.$body=i("body"),c.$header=i("#wpforms-header"),c.$page=i(t.join(",")),c.$table=c.$page.find(".wp-list-table"),c.$tableContainer=c.$table.parent(),c.$menu=i("#wpforms-list-table-ext-edit-columns-select-container"),c.$cog=d.initCogIcon(),c.$wpcontent=i("#wpcontent"),c.$tablenavPages=i(".tablenav-pages"),c.$tablenavPagesLinks=i(".tablenav-pages .pagination-links a"),c.$tableContainer.hasClass("wpforms-table-container")||(c.$table.wrap('
    '),c.$tableContainer=c.$table.parent()),c.$page.addClass("wpforms-list-table-ext-page")},initPagination(){htmx.config.historyCacheSize=2,200 th:not(:first-child):not(.wpforms-table-cell-sticky)",connectWith:"",delay:100,opacity:.75,cursor:"move",cancel:".wpforms-table-column-not-draggable",placeholder:"wpforms-table-column-drag-placeholder",appendTo:c.$page,zindex:1e4,tolerance:"intersect",distance:1,helper(e,t){var t=i(t),o=t.clone(),t=t.outerWidth();return o.css("width",t+"px")},start(e,t){t.helper.addClass("wpforms-table-column-drag-helper"),t.item.addClass("wpforms-table-column-dragged-out").css("display",""),c.$wpcontent.addClass("wpforms-no-scroll"),s=t.item.attr("id").replace("-foot","")},stop(e,t){t.item.removeClass("wpforms-table-column-drag-helper").removeClass("wpforms-table-column-dragged-out"),c.$table.find("thead tr > *, tfoot tr > *").removeClass("wpforms-table-column-drag-placeholder-prev"),c.$wpcontent.removeClass("wpforms-no-scroll");var o=t.item.prev().attr("id").replace("-foot",""),n=c.$table.find("tbody tr:not(.wpforms-hidden)"),l="cb"!==o?".column-"+o:".check-column";a=n.find("td.column-"+s).detach();for(let e=0;e *, tfoot tr > *").removeClass("wpforms-table-column-drag-placeholder-prev"),t.placeholder.prev().addClass("wpforms-table-column-drag-placeholder-prev")},update(){d.saveColumnsOrder()}})})},initTableScrollColumns(){c.$page.is("#wpforms-entries-list")&&(c.$tableScroll=c.$tableContainer,c.$tableScroll.addClass("wpforms-table-scroll"),c.$tableScroll.toggleClass("wpforms-scrollbar",d.isCustomScrollbarNeeded()),c.$table.find(".check-column, .column-indicators").addClass("wpforms-table-cell-sticky").addClass("left"),c.$table.find(".column-actions").addClass("wpforms-table-cell-sticky").addClass("right"))},tableScroll(){var e,t,o;c.$tableScroll?.length&&(e=c.$tableScroll.outerWidth(),t=Math.abs(c.$tableScroll.get(0).scrollLeft),o=c.$tableScroll.get(0).scrollWidth,c.$tableScroll.find(".wpforms-table-cell-sticky.left").toggleClass("shadow",1{var t=c.$menu.find(".wpforms-multiselect-checkbox-optgroup-"+e).nextUntil(".wpforms-multiselect-checkbox-optgroup").filter("label"),o=t.filter(function(){return i(this).is(":hidden")});c.$menu.find(".wpforms-multiselect-checkbox-no-results-"+e).toggleClass("wpforms-hidden",t.length!==o.length)})},closeMenu(){c.$cog.hasClass("active")&&(c.$cog.removeClass("active"),c.$menu.find(".wpforms-multiselect-checkbox-list").removeClass("open"),c.$searchInput.val(""),c.$searchInput[0]?.dispatchEvent(new Event("input")))},getColumnsOrder(){var e=c.$table.find("thead tr");const t=[];return e.find("th").each(function(){t.push(i(this).attr("id"))}),t},getMenuColumnsOrder(){let t=d.getColumnsOrder();const o=[];var n=[];c.$menu.find("input:checked").each(function(){o.push(i(this).val())}),t=t.map(function(e){return d.convertColumnId(e)});for(let e=0;e'+wpforms_admin.save_changes+""),d.updateMenuColumnsOrder())}),c.$searchInput=i("#wpforms-list-table-ext-edit-columns-select-container .wpforms-multiselect-checkbox-search"),c.$menu.removeClass("wpforms-hidden"))},appendNoResultsText(e){e.find(".wpforms-multiselect-checkbox-optgroup").each(function(e){var t=0===e?"fields":"meta",e=0===e?wpforms_admin.column_selector_no_fields:wpforms_admin.column_selector_no_meta;i(this).addClass("wpforms-multiselect-checkbox-optgroup-"+t).after(`${e}`)})},initCogIcon(){var e,t=c.$table.find("thead th:not(.hidden):last");return t.length?c.$cog?(t.append(c.$cog),c.$cog):(e=o.cogIcon.replace("#",""),e=i(``),t.append(e),e):i()},onClickCog(e){e.preventDefault()},onSaveChanges(e){e.preventDefault();e={nonce:wpforms_admin.nonce,action:c.$menu.find('input[name="action"]').val(),form_id:c.$menu.find('input[name="form_id"]').val(),columns:d.getMenuColumnsOrder()};d.closeMenu(),i.post(wpforms_admin.ajax_url,e).done(function(e){e.success?n.location.reload():d.displayErrorModal(e.data||wpforms_admin.unknown_error)}).fail(function(){d.displayErrorModal(wpforms_admin.server_error)})},onMenuToggle(e){i(o.cogIcon).toggleClass("active",e.detail.isOpen),c.$menu.find(".wpforms-multiselect-checkbox-no-results").addClass("wpforms-hidden"),d.positionMultiselectColumnsMenu()},positionMultiselectColumnsMenu(){c.$cog.length&&c.$menu.css({top:c.$cog.offset().top-i("#wpbody-content").offset().top+c.$cog.outerHeight()+6})},isCustomScrollbarNeeded(){var e=navigator.userAgent;return(e.includes("Windows")||e.includes("Linux"))&&(e.includes("Chrome")||e.includes("Firefox"))}};return d}(document,window,jQuery);WPFormsAdminListTableExt.init();admin/share/xor.js000064400000012102151716470030010104 0ustar00/* global define */ /* eslint-disable */ /** * XOR, or exclusive or, is a logical bitwise operation that stands for "exclusive or." * In the context of binary numbers, XOR compares corresponding bits of two operands and * produces a new result. The XOR operation returns true (or 1) for bits where the operands differ. * * Note: This class is a simple obfuscation technique and should not be used for securing sensitive data. * * Here's the truth table for XOR: * * A | B | A XOR B * ----------------- * 0 | 0 | 0 * 0 | 1 | 1 * 1 | 0 | 1 * 1 | 1 | 0 * * In binary, XOR is often denoted by the symbol ^. * Here's an example of XOR operation on binary numbers: * * 1101 (13 in decimal) * ^ 1010 (10 in decimal) * ------------------------ * 0111 (7 in decimal) * * Example Usage: * * // Instantiate the plugin with a custom encryption key. * const xorInstance = new WPFormsXOR({ * key: 55, // Use any number as the encryption key. * }); * * // Example object to encrypt. * const dataToEncrypt = { * age: 30, * name: 'Sullie', * city: 'Texas', * }; * * // Encrypt the object. * const encryptedValue = xorInstance.encrypt(dataToEncrypt); * console.log('Encrypted:', encryptedValue); * * // Decrypt the string. * const decryptedObject = xorInstance.decrypt(encryptedValue); * console.log('Decrypted:', decryptedObject); */ /* eslint-enable */ ( function( root, factory ) { const pluginName = 'WPFormsXOR'; if ( typeof define === 'function' && define.amd ) { define( [], factory( pluginName ) ); } else if ( typeof exports === 'object' ) { module.exports = factory( pluginName ); } else { root[ pluginName ] = factory( pluginName ); } // eslint-disable-next-line max-lines-per-function }( this, function( pluginName ) { // eslint-disable-next-line strict 'use strict'; /** * Plugin Error Object. * * @since 1.8.6 * * @class PluginError * * @augments Error */ class PluginError extends Error { /** * Constructor. * * @since 1.8.6 * * @param {string} message The error message. */ constructor( message ) { super( message ); this.name = pluginName; } } /** * Plugin Object. * * @since 1.8.6 * * @class Plugin */ class Plugin { // Default settings. static defaults = { // The encryption key is a crucial component in encryption algorithms, // including the XOR encryption used in the provided code. // The key is a value used to control the transformation // of the data during encryption and decryption. key: 42, // You can use any number. }; /** * Constructor. * * @since 1.8.6 * * @param {Object} args The argument object. */ constructor( args ) { // Merge the default settings with the provided settings. this.args = Object.assign( {}, Plugin.defaults, args ); } /** * Encrypt an object using XOR encryption. * * @since 1.8.6 * * @param {Object} obj The object to encrypt. * * @return {string} The encrypted object as a string. */ encrypt( obj ) { // Bail if the input is not an object. if ( typeof obj !== 'object' ) { throw new PluginError( 'Invalid input. Expected an object for encryption.' ); } // Initialize an empty string to store the encrypted result. let result = ''; try { // Convert the object to a JSON string. const jsonString = JSON.stringify( obj ); // Iterate through each character of the JSON string. for ( let i = 0; i < jsonString.length; i++ ) { // XOR each character with the encryption key and append to the result. // eslint-disable-next-line no-bitwise result += String.fromCharCode( jsonString.charCodeAt( i ) ^ this.args.key ); } } catch ( error ) { // Throw a PluginError if there's an issue during JSON stringification. throw new PluginError( 'Error during encryption. Unable to stringify the object.' ); } return result; } /** * Decrypt a string using XOR encryption. * * @since 1.8.6 * * @param {string} encryptedString The encrypted string. * * @return {Object} The decrypted object. */ decrypt( encryptedString = '' ) { // Bail if the input is not a string. if ( typeof encryptedString !== 'string' ) { throw new PluginError( 'Invalid input. Expected a string for decryption.' ); } // Bail if there is no encrypted string. if ( ! encryptedString ) { return {}; // Return an empty object. } let result = ''; try { // Iterate through each character of the encrypted string. for ( let i = 0; i < encryptedString.length; i++ ) { // XOR each character with the decryption key and append to the result. // eslint-disable-next-line no-bitwise result += String.fromCharCode( encryptedString.charCodeAt( i ) ^ this.args.key ); } // Parse the decrypted result as JSON or return an empty object if parsing fails. return JSON.parse( result || '{}' ); } catch ( error ) { // Throw an error if there's an issue during decryption or parsing. throw new PluginError( 'Error during decryption. Unable to parse decrypted data.' ); } } } return Plugin; } ) ); admin/share/xor.min.js000064400000001675151716470030010703 0ustar00!function(t,r){var e="WPFormsXOR";"function"==typeof define&&define.amd?define([],r(e)):"object"==typeof exports?module.exports=r(e):t[e]=r(e)}(this,function(r){"use strict";class n extends Error{constructor(t){super(t),this.name=r}}return class e{static defaults={key:42};constructor(t){this.args=Object.assign({},e.defaults,t)}encrypt(t){if("object"!=typeof t)throw new n("Invalid input. Expected an object for encryption.");let r="";try{var e=JSON.stringify(t);for(let t=0;t { if ( navigator.userAgent.includes( 'Safari' ) && ! navigator.userAgent.includes( 'Chrome' ) ) { $( 'html, body' ).animate( { scrollTop: 0 }, 0 ); } $( '.jconfirm-box-container' ) .css( 'padding-top', '50px' ) .animate( { opacity: 1 }, 30 ); }, 0 ); }, onOpen() { $( '.jconfirm' ).css( 'bottom', 0 ); $( '.wpforms-dash-widget-welcome-block' ).remove(); app.dismissDashboardWidgetBanner(); }, onDestroy() { $( 'body' ) .removeClass( 'wpforms-splash-modal' ) .css( '--wpforms-body-scrollbar-width', null ); }, } ); }, /** * Dismiss the dashboard widget banner. * * @since 1.9.0 */ dismissDashboardWidgetBanner() { const data = { _wpnonce: wpforms_splash_data.nonce, action : 'wpforms_dash_widget_save_widget_meta', meta: 'hide_welcome_block', value: 1, }; $.post( ajaxurl, data ); }, }; // Provide access to public functions/properties. return app; }( document, window, jQuery ) ); WPSplash.init(); admin/splash/modal.min.js000064400000002644151716470030011354 0ustar00const WPSplash=window.WPSplash||function(e,s,a){const n={init(){a(n.ready)},ready(){n.events(),wpforms_splash_data.triggerForceOpen&&n.openModal()},events(){a(e).on("click",".wpforms-splash-modal-open",function(o){o.preventDefault(),n.openModal()})},openModal(){a.alert({title:!1,content:wp.template("wpforms-splash-modal-content")(),icon:!1,closeIcon:!0,boxWidth:"1000px",theme:"modern",useBootstrap:!1,scrollToPreviousElement:!1,buttons:!1,backgroundDismiss:!0,offsetTop:50,offsetBottom:50,animation:"opacity",closeAnimation:"opacity",animateFromElement:!1,onOpenBefore(){var o=s.innerWidth-e.body.clientWidth+"px";a("body").addClass("wpforms-splash-modal").css("--wpforms-body-scrollbar-width",o),a(".wpforms-challenge-popup-container").addClass("wpforms-invisible"),setTimeout(()=>{navigator.userAgent.includes("Safari")&&!navigator.userAgent.includes("Chrome")&&a("html, body").animate({scrollTop:0},0),a(".jconfirm-box-container").css("padding-top","50px").animate({opacity:1},30)},0)},onOpen(){a(".jconfirm").css("bottom",0),a(".wpforms-dash-widget-welcome-block").remove(),n.dismissDashboardWidgetBanner()},onDestroy(){a("body").removeClass("wpforms-splash-modal").css("--wpforms-body-scrollbar-width",null)}})},dismissDashboardWidgetBanner(){var o={_wpnonce:wpforms_splash_data.nonce,action:"wpforms_dash_widget_save_widget_meta",meta:"hide_welcome_block",value:1};a.post(ajaxurl,o)}};return n}(document,window,jQuery);WPSplash.init();frontend/fields/text-limit.es5.js000064400000071523151716470030012776 0ustar00(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o -1 && words >= limit) { e.preventDefault(); } }; } /** * Get passed text from the clipboard. * * @since 1.5.6 * * @param {ClipboardEvent} e Clipboard event. * * @return {string} Text from clipboard. */ function getPastedText(e) { if (window.clipboardData && window.clipboardData.getData) { // IE return window.clipboardData.getData('Text'); } else if (e.clipboardData && e.clipboardData.getData) { return e.clipboardData.getData('text/plain'); } return ''; } /** * Paste event higher order function for character limit. * * @since 1.6.7.1 * * @param {number} limit Max allowed number of characters. * * @return {Function} Event handler. */ function pasteText(limit) { return function (e) { e.preventDefault(); var pastedText = getPastedText(e), newPosition = this.selectionStart + pastedText.length, newText = this.value.substring(0, this.selectionStart) + pastedText + this.value.substring(this.selectionStart); this.value = newText.substring(0, limit); this.setSelectionRange(newPosition, newPosition); }; } /** * Limit string length to a certain number of words, preserving line breaks. * * @since 1.6.8 * * @param {string} text Text. * @param {number} limit Max allowed number of words. * * @return {string} Text with the limited number of words. */ function limitWords(text, limit) { var result = ''; // Regular expression pattern: match any space character. var regEx = /\s+/g; // Store separators for further join. var separators = text.trim().match(regEx) || []; // Split the new text by regular expression. var newTextArray = text.split(regEx); // Limit the number of words. newTextArray.splice(limit, newTextArray.length); // Join the words together using stored separators. for (var i = 0; i < newTextArray.length; i++) { result += newTextArray[i] + (separators[i] || ''); } return result.trim(); } /** * Paste event higher order function for words limit. * * @since 1.5.6 * * @param {number} limit Max allowed number of words. * * @return {Function} Event handler. */ function pasteWords(limit) { return function (e) { e.preventDefault(); var pastedText = getPastedText(e), newPosition = this.selectionStart + pastedText.length, newText = this.value.substring(0, this.selectionStart) + pastedText + this.value.substring(this.selectionStart); this.value = limitWords(newText, limit); this.setSelectionRange(newPosition, newPosition); }; } /** * Array.from polyfill. * * @since 1.5.6 * * @param {Object} el Iterator. * * @return {Object} Array. */ function arrFrom(el) { return [].slice.call(el); } /** * Public functions and properties. * * @since 1.8.9 * * @type {Object} */ var app = { /** * Init text limit hint. * * @since 1.8.9 * * @param {string} context Context selector. */ initHint: function initHint(context) { arrFrom(document.querySelectorAll(context + ' .wpforms-limit-characters-enabled')).map(function (e) { // eslint-disable-line array-callback-return var limit = parseInt(e.dataset.textLimit, 10) || 0; e.value = e.value.slice(0, limit); var hint = createHint(e.dataset.formId, e.dataset.fieldId, renderHint(wpforms_settings.val_limit_characters, e.value.length, limit)); var fn = checkCharacters(hint, limit); e.parentNode.appendChild(hint); e.addEventListener('keydown', fn); e.addEventListener('keyup', fn); e.addEventListener('paste', pasteText(limit)); }); arrFrom(document.querySelectorAll(context + ' .wpforms-limit-words-enabled')).map(function (e) { // eslint-disable-line array-callback-return var limit = parseInt(e.dataset.textLimit, 10) || 0; e.value = limitWords(e.value, limit); var hint = createHint(e.dataset.formId, e.dataset.fieldId, renderHint(wpforms_settings.val_limit_words, countWords(e.value.trim()), limit)); var fn = checkWords(hint, limit); e.parentNode.appendChild(hint); e.addEventListener('keydown', fn); e.addEventListener('keyup', fn); e.addEventListener('paste', pasteWords(limit)); }); } }; /** * DOMContentLoaded handler. * * @since 1.5.6 */ function ready() { // Expose to the world. window.WPFormsTextLimit = app; app.initHint('body'); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', ready); } else { ready(); } })(); //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJyZW5kZXJIaW50IiwiaGludFRleHQiLCJjb3VudCIsImxpbWl0IiwicmVwbGFjZSIsImNyZWF0ZUhpbnQiLCJmb3JtSWQiLCJmaWVsZElkIiwidGV4dCIsImhpbnQiLCJkb2N1bWVudCIsImNyZWF0ZUVsZW1lbnQiLCJfdHlwZW9mIiwiY2xhc3NMaXN0IiwiYWRkIiwiaWQiLCJzZXRBdHRyaWJ1dGUiLCJ0ZXh0Q29udGVudCIsImNoZWNrQ2hhcmFjdGVycyIsImUiLCJ3aW5kb3ciLCJ3cGZvcm1zX3NldHRpbmdzIiwidmFsX2xpbWl0X2NoYXJhY3RlcnMiLCJ2YWx1ZSIsImxlbmd0aCIsImNvdW50V29yZHMiLCJzdHJpbmciLCJmb3JFYWNoIiwicGF0dGVybiIsInNwbGl0IiwiY2hlY2tXb3JkcyIsInRyaW0iLCJ3b3JkcyIsInZhbF9saW1pdF93b3JkcyIsImluZGV4T2YiLCJrZXlDb2RlIiwicHJldmVudERlZmF1bHQiLCJnZXRQYXN0ZWRUZXh0IiwiY2xpcGJvYXJkRGF0YSIsImdldERhdGEiLCJwYXN0ZVRleHQiLCJwYXN0ZWRUZXh0IiwibmV3UG9zaXRpb24iLCJzZWxlY3Rpb25TdGFydCIsIm5ld1RleHQiLCJzdWJzdHJpbmciLCJzZXRTZWxlY3Rpb25SYW5nZSIsImxpbWl0V29yZHMiLCJyZXN1bHQiLCJyZWdFeCIsInNlcGFyYXRvcnMiLCJtYXRjaCIsIm5ld1RleHRBcnJheSIsInNwbGljZSIsImkiLCJwYXN0ZVdvcmRzIiwiYXJyRnJvbSIsImVsIiwic2xpY2UiLCJjYWxsIiwiYXBwIiwiaW5pdEhpbnQiLCJjb250ZXh0IiwicXVlcnlTZWxlY3RvckFsbCIsIm1hcCIsInBhcnNlSW50IiwiZGF0YXNldCIsInRleHRMaW1pdCIsImZuIiwicGFyZW50Tm9kZSIsImFwcGVuZENoaWxkIiwiYWRkRXZlbnRMaXN0ZW5lciIsInJlYWR5IiwiV1BGb3Jtc1RleHRMaW1pdCIsInJlYWR5U3RhdGUiXSwic291cmNlcyI6WyJmYWtlX2M0YmFiMjc2LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qIGdsb2JhbCB3cGZvcm1zX3NldHRpbmdzICovXG5cbiggZnVuY3Rpb24oKSB7XG5cdC8qKlxuXHQgKiBQcmVkZWZpbmUgaGludCB0ZXh0IHRvIGRpc3BsYXkuXG5cdCAqXG5cdCAqIEBzaW5jZSAxLjUuNlxuXHQgKiBAc2luY2UgMS42LjQgQWRkZWQgYSBuZXcgbWFjcm9zIC0ge3JlbWFpbmluZ30uXG5cdCAqXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSBoaW50VGV4dCBIaW50IHRleHQuXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSBjb3VudCAgICBDdXJyZW50IGNvdW50LlxuXHQgKiBAcGFyYW0ge251bWJlcn0gbGltaXQgICAgTGltaXQgdG8uXG5cdCAqXG5cdCAqIEByZXR1cm4ge3N0cmluZ30gUHJlZGVmaW5lZCBoaW50IHRleHQuXG5cdCAqL1xuXHRmdW5jdGlvbiByZW5kZXJIaW50KCBoaW50VGV4dCwgY291bnQsIGxpbWl0ICkge1xuXHRcdHJldHVybiBoaW50VGV4dC5yZXBsYWNlKCAne2NvdW50fScsIGNvdW50ICkucmVwbGFjZSggJ3tsaW1pdH0nLCBsaW1pdCApLnJlcGxhY2UoICd7cmVtYWluaW5nfScsIGxpbWl0IC0gY291bnQgKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDcmVhdGUgSFRNTEVsZW1lbnQgaGludCBlbGVtZW50IHdpdGggdGV4dC5cblx0ICpcblx0ICogQHNpbmNlIDEuNS42XG5cdCAqXG5cdCAqIEBwYXJhbSB7bnVtYmVyfHN0cmluZ30gZm9ybUlkICBGb3JtIGlkLlxuXHQgKiBAcGFyYW0ge251bWJlcnxzdHJpbmd9IGZpZWxkSWQgRm9ybSBmaWVsZCBpZC5cblx0ICogQHBhcmFtIHtzdHJpbmd9ICAgICAgICB0ZXh0ICAgIEhpbnQgdGV4dC5cblx0ICpcblx0ICogQHJldHVybiB7T2JqZWN0fSBIVE1MRWxlbWVudCBoaW50IGVsZW1lbnQgd2l0aCB0ZXh0LlxuXHQgKi9cblx0ZnVuY3Rpb24gY3JlYXRlSGludCggZm9ybUlkLCBmaWVsZElkLCB0ZXh0ICkge1xuXHRcdGNvbnN0IGhpbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCAnZGl2JyApO1xuXG5cdFx0Zm9ybUlkID0gdHlwZW9mIGZvcm1JZCA9PT0gJ29iamVjdCcgPyAnJyA6IGZvcm1JZDtcblx0XHRmaWVsZElkID0gdHlwZW9mIGZpZWxkSWQgPT09ICdvYmplY3QnID8gJycgOiBmaWVsZElkO1xuXG5cdFx0aGludC5jbGFzc0xpc3QuYWRkKCAnd3Bmb3Jtcy1maWVsZC1saW1pdC10ZXh0JyApO1xuXHRcdGhpbnQuaWQgPSAnd3Bmb3Jtcy1maWVsZC1saW1pdC10ZXh0LScgKyBmb3JtSWQgKyAnLScgKyBmaWVsZElkO1xuXHRcdGhpbnQuc2V0QXR0cmlidXRlKCAnYXJpYS1saXZlJywgJ3BvbGl0ZScgKTtcblx0XHRoaW50LnRleHRDb250ZW50ID0gdGV4dDtcblxuXHRcdHJldHVybiBoaW50O1xuXHR9XG5cblx0LyoqXG5cdCAqIEtleXVwL0tleWRvd24gZXZlbnQgaGlnaGVyIG9yZGVyIGZ1bmN0aW9uIGZvciBjaGFyYWN0ZXJzIGxpbWl0LlxuXHQgKlxuXHQgKiBAc2luY2UgMS41LjZcblx0ICpcblx0ICogQHBhcmFtIHtPYmplY3R9IGhpbnQgIEhUTUxFbGVtZW50IGhpbnQgZWxlbWVudC5cblx0ICogQHBhcmFtIHtudW1iZXJ9IGxpbWl0IE1heCBhbGxvd2VkIG51bWJlciBvZiBjaGFyYWN0ZXJzLlxuXHQgKlxuXHQgKiBAcmV0dXJuIHtGdW5jdGlvbn0gSGFuZGxlciBmdW5jdGlvbi5cblx0ICovXG5cdGZ1bmN0aW9uIGNoZWNrQ2hhcmFjdGVycyggaGludCwgbGltaXQgKSB7XG5cdFx0Ly8gbm9pbnNwZWN0aW9uIEpTVW51c2VkTG9jYWxTeW1ib2xzXG5cdFx0cmV0dXJuIGZ1bmN0aW9uKCBlICkgeyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLXVudXNlZC12YXJzXG5cdFx0XHRoaW50LnRleHRDb250ZW50ID0gcmVuZGVySGludChcblx0XHRcdFx0d2luZG93LndwZm9ybXNfc2V0dGluZ3MudmFsX2xpbWl0X2NoYXJhY3RlcnMsXG5cdFx0XHRcdHRoaXMudmFsdWUubGVuZ3RoLFxuXHRcdFx0XHRsaW1pdFxuXHRcdFx0KTtcblx0XHR9O1xuXHR9XG5cblx0LyoqXG5cdCAqIENvdW50IHdvcmRzIGluIHRoZSBzdHJpbmcuXG5cdCAqXG5cdCAqIEBzaW5jZSAxLjYuMlxuXHQgKlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gc3RyaW5nIFN0cmluZyB2YWx1ZS5cblx0ICpcblx0ICogQHJldHVybiB7bnVtYmVyfSBXb3JkcyBjb3VudC5cblx0ICovXG5cdGZ1bmN0aW9uIGNvdW50V29yZHMoIHN0cmluZyApIHtcblx0XHRpZiAoIHR5cGVvZiBzdHJpbmcgIT09ICdzdHJpbmcnICkge1xuXHRcdFx0cmV0dXJuIDA7XG5cdFx0fVxuXG5cdFx0aWYgKCAhIHN0cmluZy5sZW5ndGggKSB7XG5cdFx0XHRyZXR1cm4gMDtcblx0XHR9XG5cblx0XHRbXG5cdFx0XHQvKFtBLVpdKyksKFtBLVpdKykvZ2ksXG5cdFx0XHQvKFswLTldKyksKFtBLVpdKykvZ2ksXG5cdFx0XHQvKFtBLVpdKyksKFswLTldKykvZ2ksXG5cdFx0XS5mb3JFYWNoKCBmdW5jdGlvbiggcGF0dGVybiApIHtcblx0XHRcdHN0cmluZyA9IHN0cmluZy5yZXBsYWNlKCBwYXR0ZXJuLCAnJDEsICQyJyApO1xuXHRcdH0gKTtcblxuXHRcdHJldHVybiBzdHJpbmcuc3BsaXQoIC9cXHMrLyApLmxlbmd0aDtcblx0fVxuXG5cdC8qKlxuXHQgKiBLZXl1cC9LZXlkb3duIGV2ZW50IGhpZ2hlciBvcmRlciBmdW5jdGlvbiBmb3Igd29yZHMgbGltaXQuXG5cdCAqXG5cdCAqIEBzaW5jZSAxLjUuNlxuXHQgKlxuXHQgKiBAcGFyYW0ge09iamVjdH0gaGludCAgSFRNTEVsZW1lbnQgaGludCBlbGVtZW50LlxuXHQgKiBAcGFyYW0ge251bWJlcn0gbGltaXQgTWF4IGFsbG93ZWQgbnVtYmVyIG9mIGNoYXJhY3RlcnMuXG5cdCAqXG5cdCAqIEByZXR1cm4ge0Z1bmN0aW9ufSBIYW5kbGVyIGZ1bmN0aW9uLlxuXHQgKi9cblx0ZnVuY3Rpb24gY2hlY2tXb3JkcyggaGludCwgbGltaXQgKSB7XG5cdFx0cmV0dXJuIGZ1bmN0aW9uKCBlICkge1xuXHRcdFx0Y29uc3QgdmFsdWUgPSB0aGlzLnZhbHVlLnRyaW0oKSxcblx0XHRcdFx0d29yZHMgPSBjb3VudFdvcmRzKCB2YWx1ZSApO1xuXG5cdFx0XHRoaW50LnRleHRDb250ZW50ID0gcmVuZGVySGludChcblx0XHRcdFx0d2luZG93LndwZm9ybXNfc2V0dGluZ3MudmFsX2xpbWl0X3dvcmRzLFxuXHRcdFx0XHR3b3Jkcyxcblx0XHRcdFx0bGltaXRcblx0XHRcdCk7XG5cblx0XHRcdC8vIFdlIHNob3VsZCBwcmV2ZW50IHRoZSBrZXlzOiBFbnRlciwgU3BhY2UsIENvbW1hLlxuXHRcdFx0aWYgKCBbIDEzLCAzMiwgMTg4IF0uaW5kZXhPZiggZS5rZXlDb2RlICkgPiAtMSAmJiB3b3JkcyA+PSBsaW1pdCApIHtcblx0XHRcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpO1xuXHRcdFx0fVxuXHRcdH07XG5cdH1cblxuXHQvKipcblx0ICogR2V0IHBhc3NlZCB0ZXh0IGZyb20gdGhlIGNsaXBib2FyZC5cblx0ICpcblx0ICogQHNpbmNlIDEuNS42XG5cdCAqXG5cdCAqIEBwYXJhbSB7Q2xpcGJvYXJkRXZlbnR9IGUgQ2xpcGJvYXJkIGV2ZW50LlxuXHQgKlxuXHQgKiBAcmV0dXJuIHtzdHJpbmd9IFRleHQgZnJvbSBjbGlwYm9hcmQuXG5cdCAqL1xuXHRmdW5jdGlvbiBnZXRQYXN0ZWRUZXh0KCBlICkge1xuXHRcdGlmICggd2luZG93LmNsaXBib2FyZERhdGEgJiYgd2luZG93LmNsaXBib2FyZERhdGEuZ2V0RGF0YSApIHsgLy8gSUVcblx0XHRcdHJldHVybiB3aW5kb3cuY2xpcGJvYXJkRGF0YS5nZXREYXRhKCAnVGV4dCcgKTtcblx0XHR9IGVsc2UgaWYgKCBlLmNsaXBib2FyZERhdGEgJiYgZS5jbGlwYm9hcmREYXRhLmdldERhdGEgKSB7XG5cdFx0XHRyZXR1cm4gZS5jbGlwYm9hcmREYXRhLmdldERhdGEoICd0ZXh0L3BsYWluJyApO1xuXHRcdH1cblxuXHRcdHJldHVybiAnJztcblx0fVxuXG5cdC8qKlxuXHQgKiBQYXN0ZSBldmVudCBoaWdoZXIgb3JkZXIgZnVuY3Rpb24gZm9yIGNoYXJhY3RlciBsaW1pdC5cblx0ICpcblx0ICogQHNpbmNlIDEuNi43LjFcblx0ICpcblx0ICogQHBhcmFtIHtudW1iZXJ9IGxpbWl0IE1heCBhbGxvd2VkIG51bWJlciBvZiBjaGFyYWN0ZXJzLlxuXHQgKlxuXHQgKiBAcmV0dXJuIHtGdW5jdGlvbn0gRXZlbnQgaGFuZGxlci5cblx0ICovXG5cdGZ1bmN0aW9uIHBhc3RlVGV4dCggbGltaXQgKSB7XG5cdFx0cmV0dXJuIGZ1bmN0aW9uKCBlICkge1xuXHRcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpO1xuXG5cdFx0XHRjb25zdCBwYXN0ZWRUZXh0ID0gZ2V0UGFzdGVkVGV4dCggZSApLFxuXHRcdFx0XHRuZXdQb3NpdGlvbiA9IHRoaXMuc2VsZWN0aW9uU3RhcnQgKyBwYXN0ZWRUZXh0Lmxlbmd0aCxcblx0XHRcdFx0bmV3VGV4dCA9IHRoaXMudmFsdWUuc3Vic3RyaW5nKCAwLCB0aGlzLnNlbGVjdGlvblN0YXJ0ICkgKyBwYXN0ZWRUZXh0ICsgdGhpcy52YWx1ZS5zdWJzdHJpbmcoIHRoaXMuc2VsZWN0aW9uU3RhcnQgKTtcblxuXHRcdFx0dGhpcy52YWx1ZSA9IG5ld1RleHQuc3Vic3RyaW5nKCAwLCBsaW1pdCApO1xuXHRcdFx0dGhpcy5zZXRTZWxlY3Rpb25SYW5nZSggbmV3UG9zaXRpb24sIG5ld1Bvc2l0aW9uICk7XG5cdFx0fTtcblx0fVxuXG5cdC8qKlxuXHQgKiBMaW1pdCBzdHJpbmcgbGVuZ3RoIHRvIGEgY2VydGFpbiBudW1iZXIgb2Ygd29yZHMsIHByZXNlcnZpbmcgbGluZSBicmVha3MuXG5cdCAqXG5cdCAqIEBzaW5jZSAxLjYuOFxuXHQgKlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gdGV4dCAgVGV4dC5cblx0ICogQHBhcmFtIHtudW1iZXJ9IGxpbWl0IE1heCBhbGxvd2VkIG51bWJlciBvZiB3b3Jkcy5cblx0ICpcblx0ICogQHJldHVybiB7c3RyaW5nfSBUZXh0IHdpdGggdGhlIGxpbWl0ZWQgbnVtYmVyIG9mIHdvcmRzLlxuXHQgKi9cblx0ZnVuY3Rpb24gbGltaXRXb3JkcyggdGV4dCwgbGltaXQgKSB7XG5cdFx0bGV0IHJlc3VsdCA9ICcnO1xuXG5cdFx0Ly8gUmVndWxhciBleHByZXNzaW9uIHBhdHRlcm46IG1hdGNoIGFueSBzcGFjZSBjaGFyYWN0ZXIuXG5cdFx0Y29uc3QgcmVnRXggPSAvXFxzKy9nO1xuXG5cdFx0Ly8gU3RvcmUgc2VwYXJhdG9ycyBmb3IgZnVydGhlciBqb2luLlxuXHRcdGNvbnN0IHNlcGFyYXRvcnMgPSB0ZXh0LnRyaW0oKS5tYXRjaCggcmVnRXggKSB8fCBbXTtcblxuXHRcdC8vIFNwbGl0IHRoZSBuZXcgdGV4dCBieSByZWd1bGFyIGV4cHJlc3Npb24uXG5cdFx0Y29uc3QgbmV3VGV4dEFycmF5ID0gdGV4dC5zcGxpdCggcmVnRXggKTtcblxuXHRcdC8vIExpbWl0IHRoZSBudW1iZXIgb2Ygd29yZHMuXG5cdFx0bmV3VGV4dEFycmF5LnNwbGljZSggbGltaXQsIG5ld1RleHRBcnJheS5sZW5ndGggKTtcblxuXHRcdC8vIEpvaW4gdGhlIHdvcmRzIHRvZ2V0aGVyIHVzaW5nIHN0b3JlZCBzZXBhcmF0b3JzLlxuXHRcdGZvciAoIGxldCBpID0gMDsgaSA8IG5ld1RleHRBcnJheS5sZW5ndGg7IGkrKyApIHtcblx0XHRcdHJlc3VsdCArPSBuZXdUZXh0QXJyYXlbIGkgXSArICggc2VwYXJhdG9yc1sgaSBdIHx8ICcnICk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHJlc3VsdC50cmltKCk7XG5cdH1cblxuXHQvKipcblx0ICogUGFzdGUgZXZlbnQgaGlnaGVyIG9yZGVyIGZ1bmN0aW9uIGZvciB3b3JkcyBsaW1pdC5cblx0ICpcblx0ICogQHNpbmNlIDEuNS42XG5cdCAqXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSBsaW1pdCBNYXggYWxsb3dlZCBudW1iZXIgb2Ygd29yZHMuXG5cdCAqXG5cdCAqIEByZXR1cm4ge0Z1bmN0aW9ufSBFdmVudCBoYW5kbGVyLlxuXHQgKi9cblx0ZnVuY3Rpb24gcGFzdGVXb3JkcyggbGltaXQgKSB7XG5cdFx0cmV0dXJuIGZ1bmN0aW9uKCBlICkge1xuXHRcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpO1xuXG5cdFx0XHRjb25zdCBwYXN0ZWRUZXh0ID0gZ2V0UGFzdGVkVGV4dCggZSApLFxuXHRcdFx0XHRuZXdQb3NpdGlvbiA9IHRoaXMuc2VsZWN0aW9uU3RhcnQgKyBwYXN0ZWRUZXh0Lmxlbmd0aCxcblx0XHRcdFx0bmV3VGV4dCA9IHRoaXMudmFsdWUuc3Vic3RyaW5nKCAwLCB0aGlzLnNlbGVjdGlvblN0YXJ0ICkgKyBwYXN0ZWRUZXh0ICsgdGhpcy52YWx1ZS5zdWJzdHJpbmcoIHRoaXMuc2VsZWN0aW9uU3RhcnQgKTtcblxuXHRcdFx0dGhpcy52YWx1ZSA9IGxpbWl0V29yZHMoIG5ld1RleHQsIGxpbWl0ICk7XG5cdFx0XHR0aGlzLnNldFNlbGVjdGlvblJhbmdlKCBuZXdQb3NpdGlvbiwgbmV3UG9zaXRpb24gKTtcblx0XHR9O1xuXHR9XG5cblx0LyoqXG5cdCAqIEFycmF5LmZyb20gcG9seWZpbGwuXG5cdCAqXG5cdCAqIEBzaW5jZSAxLjUuNlxuXHQgKlxuXHQgKiBAcGFyYW0ge09iamVjdH0gZWwgSXRlcmF0b3IuXG5cdCAqXG5cdCAqIEByZXR1cm4ge09iamVjdH0gQXJyYXkuXG5cdCAqL1xuXHRmdW5jdGlvbiBhcnJGcm9tKCBlbCApIHtcblx0XHRyZXR1cm4gW10uc2xpY2UuY2FsbCggZWwgKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBQdWJsaWMgZnVuY3Rpb25zIGFuZCBwcm9wZXJ0aWVzLlxuXHQgKlxuXHQgKiBAc2luY2UgMS44Ljlcblx0ICpcblx0ICogQHR5cGUge09iamVjdH1cblx0ICovXG5cdGNvbnN0IGFwcCA9IHtcblx0XHQvKipcblx0XHQgKiBJbml0IHRleHQgbGltaXQgaGludC5cblx0XHQgKlxuXHRcdCAqIEBzaW5jZSAxLjguOVxuXHRcdCAqXG5cdFx0ICogQHBhcmFtIHtzdHJpbmd9IGNvbnRleHQgQ29udGV4dCBzZWxlY3Rvci5cblx0XHQgKi9cblx0XHRpbml0SGludCggY29udGV4dCApIHtcblx0XHRcdGFyckZyb20oIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoIGNvbnRleHQgKyAnIC53cGZvcm1zLWxpbWl0LWNoYXJhY3RlcnMtZW5hYmxlZCcgKSApXG5cdFx0XHRcdC5tYXAoXG5cdFx0XHRcdFx0ZnVuY3Rpb24oIGUgKSB7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgYXJyYXktY2FsbGJhY2stcmV0dXJuXG5cdFx0XHRcdFx0XHRjb25zdCBsaW1pdCA9IHBhcnNlSW50KCBlLmRhdGFzZXQudGV4dExpbWl0LCAxMCApIHx8IDA7XG5cblx0XHRcdFx0XHRcdGUudmFsdWUgPSBlLnZhbHVlLnNsaWNlKCAwLCBsaW1pdCApO1xuXG5cdFx0XHRcdFx0XHRjb25zdCBoaW50ID0gY3JlYXRlSGludChcblx0XHRcdFx0XHRcdFx0ZS5kYXRhc2V0LmZvcm1JZCxcblx0XHRcdFx0XHRcdFx0ZS5kYXRhc2V0LmZpZWxkSWQsXG5cdFx0XHRcdFx0XHRcdHJlbmRlckhpbnQoXG5cdFx0XHRcdFx0XHRcdFx0d3Bmb3Jtc19zZXR0aW5ncy52YWxfbGltaXRfY2hhcmFjdGVycyxcblx0XHRcdFx0XHRcdFx0XHRlLnZhbHVlLmxlbmd0aCxcblx0XHRcdFx0XHRcdFx0XHRsaW1pdFxuXHRcdFx0XHRcdFx0XHQpXG5cdFx0XHRcdFx0XHQpO1xuXG5cdFx0XHRcdFx0XHRjb25zdCBmbiA9IGNoZWNrQ2hhcmFjdGVycyggaGludCwgbGltaXQgKTtcblxuXHRcdFx0XHRcdFx0ZS5wYXJlbnROb2RlLmFwcGVuZENoaWxkKCBoaW50ICk7XG5cdFx0XHRcdFx0XHRlLmFkZEV2ZW50TGlzdGVuZXIoICdrZXlkb3duJywgZm4gKTtcblx0XHRcdFx0XHRcdGUuYWRkRXZlbnRMaXN0ZW5lciggJ2tleXVwJywgZm4gKTtcblx0XHRcdFx0XHRcdGUuYWRkRXZlbnRMaXN0ZW5lciggJ3Bhc3RlJywgcGFzdGVUZXh0KCBsaW1pdCApICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHQpO1xuXG5cdFx0XHRhcnJGcm9tKCBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKCBjb250ZXh0ICsgJyAud3Bmb3Jtcy1saW1pdC13b3Jkcy1lbmFibGVkJyApIClcblx0XHRcdFx0Lm1hcChcblx0XHRcdFx0XHRmdW5jdGlvbiggZSApIHsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBhcnJheS1jYWxsYmFjay1yZXR1cm5cblx0XHRcdFx0XHRcdGNvbnN0IGxpbWl0ID0gcGFyc2VJbnQoIGUuZGF0YXNldC50ZXh0TGltaXQsIDEwICkgfHwgMDtcblxuXHRcdFx0XHRcdFx0ZS52YWx1ZSA9IGxpbWl0V29yZHMoIGUudmFsdWUsIGxpbWl0ICk7XG5cblx0XHRcdFx0XHRcdGNvbnN0IGhpbnQgPSBjcmVhdGVIaW50KFxuXHRcdFx0XHRcdFx0XHRlLmRhdGFzZXQuZm9ybUlkLFxuXHRcdFx0XHRcdFx0XHRlLmRhdGFzZXQuZmllbGRJZCxcblx0XHRcdFx0XHRcdFx0cmVuZGVySGludChcblx0XHRcdFx0XHRcdFx0XHR3cGZvcm1zX3NldHRpbmdzLnZhbF9saW1pdF93b3Jkcyxcblx0XHRcdFx0XHRcdFx0XHRjb3VudFdvcmRzKCBlLnZhbHVlLnRyaW0oKSApLFxuXHRcdFx0XHRcdFx0XHRcdGxpbWl0XG5cdFx0XHRcdFx0XHRcdClcblx0XHRcdFx0XHRcdCk7XG5cblx0XHRcdFx0XHRcdGNvbnN0IGZuID0gY2hlY2tXb3JkcyggaGludCwgbGltaXQgKTtcblxuXHRcdFx0XHRcdFx0ZS5wYXJlbnROb2RlLmFwcGVuZENoaWxkKCBoaW50ICk7XG5cblx0XHRcdFx0XHRcdGUuYWRkRXZlbnRMaXN0ZW5lciggJ2tleWRvd24nLCBmbiApO1xuXHRcdFx0XHRcdFx0ZS5hZGRFdmVudExpc3RlbmVyKCAna2V5dXAnLCBmbiApO1xuXHRcdFx0XHRcdFx0ZS5hZGRFdmVudExpc3RlbmVyKCAncGFzdGUnLCBwYXN0ZVdvcmRzKCBsaW1pdCApICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHQpO1xuXHRcdH0sXG5cdH07XG5cblx0LyoqXG5cdCAqIERPTUNvbnRlbnRMb2FkZWQgaGFuZGxlci5cblx0ICpcblx0ICogQHNpbmNlIDEuNS42XG5cdCAqL1xuXHRmdW5jdGlvbiByZWFkeSgpIHtcblx0XHQvLyBFeHBvc2UgdG8gdGhlIHdvcmxkLlxuXHRcdHdpbmRvdy5XUEZvcm1zVGV4dExpbWl0ID0gYXBwO1xuXG5cdFx0YXBwLmluaXRIaW50KCAnYm9keScgKTtcblx0fVxuXG5cdGlmICggZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnICkge1xuXHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoICdET01Db250ZW50TG9hZGVkJywgcmVhZHkgKTtcblx0fSBlbHNlIHtcblx0XHRyZWFkeSgpO1xuXHR9XG59KCkgKTtcbiJdLCJtYXBwaW5ncyI6Ijs7O0FBQUE7O0FBRUUsYUFBVztFQUNaO0FBQ0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtFQUNDLFNBQVNBLFVBQVVBLENBQUVDLFFBQVEsRUFBRUMsS0FBSyxFQUFFQyxLQUFLLEVBQUc7SUFDN0MsT0FBT0YsUUFBUSxDQUFDRyxPQUFPLENBQUUsU0FBUyxFQUFFRixLQUFNLENBQUMsQ0FBQ0UsT0FBTyxDQUFFLFNBQVMsRUFBRUQsS0FBTSxDQUFDLENBQUNDLE9BQU8sQ0FBRSxhQUFhLEVBQUVELEtBQUssR0FBR0QsS0FBTSxDQUFDO0VBQ2hIOztFQUVBO0FBQ0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7RUFDQyxTQUFTRyxVQUFVQSxDQUFFQyxNQUFNLEVBQUVDLE9BQU8sRUFBRUMsSUFBSSxFQUFHO0lBQzVDLElBQU1DLElBQUksR0FBR0MsUUFBUSxDQUFDQyxhQUFhLENBQUUsS0FBTSxDQUFDO0lBRTVDTCxNQUFNLEdBQUdNLE9BQUEsQ0FBT04sTUFBTSxNQUFLLFFBQVEsR0FBRyxFQUFFLEdBQUdBLE1BQU07SUFDakRDLE9BQU8sR0FBR0ssT0FBQSxDQUFPTCxPQUFPLE1BQUssUUFBUSxHQUFHLEVBQUUsR0FBR0EsT0FBTztJQUVwREUsSUFBSSxDQUFDSSxTQUFTLENBQUNDLEdBQUcsQ0FBRSwwQkFBMkIsQ0FBQztJQUNoREwsSUFBSSxDQUFDTSxFQUFFLEdBQUcsMkJBQTJCLEdBQUdULE1BQU0sR0FBRyxHQUFHLEdBQUdDLE9BQU87SUFDOURFLElBQUksQ0FBQ08sWUFBWSxDQUFFLFdBQVcsRUFBRSxRQUFTLENBQUM7SUFDMUNQLElBQUksQ0FBQ1EsV0FBVyxHQUFHVCxJQUFJO0lBRXZCLE9BQU9DLElBQUk7RUFDWjs7RUFFQTtBQUNEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtFQUNDLFNBQVNTLGVBQWVBLENBQUVULElBQUksRUFBRU4sS0FBSyxFQUFHO0lBQ3ZDO0lBQ0EsT0FBTyxVQUFVZ0IsQ0FBQyxFQUFHO01BQUU7TUFDdEJWLElBQUksQ0FBQ1EsV0FBVyxHQUFHakIsVUFBVSxDQUM1Qm9CLE1BQU0sQ0FBQ0MsZ0JBQWdCLENBQUNDLG9CQUFvQixFQUM1QyxJQUFJLENBQUNDLEtBQUssQ0FBQ0MsTUFBTSxFQUNqQnJCLEtBQ0QsQ0FBQztJQUNGLENBQUM7RUFDRjs7RUFFQTtBQUNEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7RUFDQyxTQUFTc0IsVUFBVUEsQ0FBRUMsTUFBTSxFQUFHO0lBQzdCLElBQUssT0FBT0EsTUFBTSxLQUFLLFFBQVEsRUFBRztNQUNqQyxPQUFPLENBQUM7SUFDVDtJQUVBLElBQUssQ0FBRUEsTUFBTSxDQUFDRixNQUFNLEVBQUc7TUFDdEIsT0FBTyxDQUFDO0lBQ1Q7SUFFQSxDQUNDLHFCQUFxQixFQUNyQixxQkFBcUIsRUFDckIscUJBQXFCLENBQ3JCLENBQUNHLE9BQU8sQ0FBRSxVQUFVQyxPQUFPLEVBQUc7TUFDOUJGLE1BQU0sR0FBR0EsTUFBTSxDQUFDdEIsT0FBTyxDQUFFd0IsT0FBTyxFQUFFLFFBQVMsQ0FBQztJQUM3QyxDQUFFLENBQUM7SUFFSCxPQUFPRixNQUFNLENBQUNHLEtBQUssQ0FBRSxLQUFNLENBQUMsQ0FBQ0wsTUFBTTtFQUNwQzs7RUFFQTtBQUNEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtFQUNDLFNBQVNNLFVBQVVBLENBQUVyQixJQUFJLEVBQUVOLEtBQUssRUFBRztJQUNsQyxPQUFPLFVBQVVnQixDQUFDLEVBQUc7TUFDcEIsSUFBTUksS0FBSyxHQUFHLElBQUksQ0FBQ0EsS0FBSyxDQUFDUSxJQUFJLENBQUMsQ0FBQztRQUM5QkMsS0FBSyxHQUFHUCxVQUFVLENBQUVGLEtBQU0sQ0FBQztNQUU1QmQsSUFBSSxDQUFDUSxXQUFXLEdBQUdqQixVQUFVLENBQzVCb0IsTUFBTSxDQUFDQyxnQkFBZ0IsQ0FBQ1ksZUFBZSxFQUN2Q0QsS0FBSyxFQUNMN0IsS0FDRCxDQUFDOztNQUVEO01BQ0EsSUFBSyxDQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsR0FBRyxDQUFFLENBQUMrQixPQUFPLENBQUVmLENBQUMsQ0FBQ2dCLE9BQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJSCxLQUFLLElBQUk3QixLQUFLLEVBQUc7UUFDbEVnQixDQUFDLENBQUNpQixjQUFjLENBQUMsQ0FBQztNQUNuQjtJQUNELENBQUM7RUFDRjs7RUFFQTtBQUNEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7RUFDQyxTQUFTQyxhQUFhQSxDQUFFbEIsQ0FBQyxFQUFHO0lBQzNCLElBQUtDLE1BQU0sQ0FBQ2tCLGFBQWEsSUFBSWxCLE1BQU0sQ0FBQ2tCLGFBQWEsQ0FBQ0MsT0FBTyxFQUFHO01BQUU7TUFDN0QsT0FBT25CLE1BQU0sQ0FBQ2tCLGFBQWEsQ0FBQ0MsT0FBTyxDQUFFLE1BQU8sQ0FBQztJQUM5QyxDQUFDLE1BQU0sSUFBS3BCLENBQUMsQ0FBQ21CLGFBQWEsSUFBSW5CLENBQUMsQ0FBQ21CLGFBQWEsQ0FBQ0MsT0FBTyxFQUFHO01BQ3hELE9BQU9wQixDQUFDLENBQUNtQixhQUFhLENBQUNDLE9BQU8sQ0FBRSxZQUFhLENBQUM7SUFDL0M7SUFFQSxPQUFPLEVBQUU7RUFDVjs7RUFFQTtBQUNEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7RUFDQyxTQUFTQyxTQUFTQSxDQUFFckMsS0FBSyxFQUFHO0lBQzNCLE9BQU8sVUFBVWdCLENBQUMsRUFBRztNQUNwQkEsQ0FBQyxDQUFDaUIsY0FBYyxDQUFDLENBQUM7TUFFbEIsSUFBTUssVUFBVSxHQUFHSixhQUFhLENBQUVsQixDQUFFLENBQUM7UUFDcEN1QixXQUFXLEdBQUcsSUFBSSxDQUFDQyxjQUFjLEdBQUdGLFVBQVUsQ0FBQ2pCLE1BQU07UUFDckRvQixPQUFPLEdBQUcsSUFBSSxDQUFDckIsS0FBSyxDQUFDc0IsU0FBUyxDQUFFLENBQUMsRUFBRSxJQUFJLENBQUNGLGNBQWUsQ0FBQyxHQUFHRixVQUFVLEdBQUcsSUFBSSxDQUFDbEIsS0FBSyxDQUFDc0IsU0FBUyxDQUFFLElBQUksQ0FBQ0YsY0FBZSxDQUFDO01BRXBILElBQUksQ0FBQ3BCLEtBQUssR0FBR3FCLE9BQU8sQ0FBQ0MsU0FBUyxDQUFFLENBQUMsRUFBRTFDLEtBQU0sQ0FBQztNQUMxQyxJQUFJLENBQUMyQyxpQkFBaUIsQ0FBRUosV0FBVyxFQUFFQSxXQUFZLENBQUM7SUFDbkQsQ0FBQztFQUNGOztFQUVBO0FBQ0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0VBQ0MsU0FBU0ssVUFBVUEsQ0FBRXZDLElBQUksRUFBRUwsS0FBSyxFQUFHO0lBQ2xDLElBQUk2QyxNQUFNLEdBQUcsRUFBRTs7SUFFZjtJQUNBLElBQU1DLEtBQUssR0FBRyxNQUFNOztJQUVwQjtJQUNBLElBQU1DLFVBQVUsR0FBRzFDLElBQUksQ0FBQ3VCLElBQUksQ0FBQyxDQUFDLENBQUNvQixLQUFLLENBQUVGLEtBQU0sQ0FBQyxJQUFJLEVBQUU7O0lBRW5EO0lBQ0EsSUFBTUcsWUFBWSxHQUFHNUMsSUFBSSxDQUFDcUIsS0FBSyxDQUFFb0IsS0FBTSxDQUFDOztJQUV4QztJQUNBRyxZQUFZLENBQUNDLE1BQU0sQ0FBRWxELEtBQUssRUFBRWlELFlBQVksQ0FBQzVCLE1BQU8sQ0FBQzs7SUFFakQ7SUFDQSxLQUFNLElBQUk4QixDQUFDLEdBQUcsQ0FBQyxFQUFFQSxDQUFDLEdBQUdGLFlBQVksQ0FBQzVCLE1BQU0sRUFBRThCLENBQUMsRUFBRSxFQUFHO01BQy9DTixNQUFNLElBQUlJLFlBQVksQ0FBRUUsQ0FBQyxDQUFFLElBQUtKLFVBQVUsQ0FBRUksQ0FBQyxDQUFFLElBQUksRUFBRSxDQUFFO0lBQ3hEO0lBRUEsT0FBT04sTUFBTSxDQUFDakIsSUFBSSxDQUFDLENBQUM7RUFDckI7O0VBRUE7QUFDRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0VBQ0MsU0FBU3dCLFVBQVVBLENBQUVwRCxLQUFLLEVBQUc7SUFDNUIsT0FBTyxVQUFVZ0IsQ0FBQyxFQUFHO01BQ3BCQSxDQUFDLENBQUNpQixjQUFjLENBQUMsQ0FBQztNQUVsQixJQUFNSyxVQUFVLEdBQUdKLGFBQWEsQ0FBRWxCLENBQUUsQ0FBQztRQUNwQ3VCLFdBQVcsR0FBRyxJQUFJLENBQUNDLGNBQWMsR0FBR0YsVUFBVSxDQUFDakIsTUFBTTtRQUNyRG9CLE9BQU8sR0FBRyxJQUFJLENBQUNyQixLQUFLLENBQUNzQixTQUFTLENBQUUsQ0FBQyxFQUFFLElBQUksQ0FBQ0YsY0FBZSxDQUFDLEdBQUdGLFVBQVUsR0FBRyxJQUFJLENBQUNsQixLQUFLLENBQUNzQixTQUFTLENBQUUsSUFBSSxDQUFDRixjQUFlLENBQUM7TUFFcEgsSUFBSSxDQUFDcEIsS0FBSyxHQUFHd0IsVUFBVSxDQUFFSCxPQUFPLEVBQUV6QyxLQUFNLENBQUM7TUFDekMsSUFBSSxDQUFDMkMsaUJBQWlCLENBQUVKLFdBQVcsRUFBRUEsV0FBWSxDQUFDO0lBQ25ELENBQUM7RUFDRjs7RUFFQTtBQUNEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7RUFDQyxTQUFTYyxPQUFPQSxDQUFFQyxFQUFFLEVBQUc7SUFDdEIsT0FBTyxFQUFFLENBQUNDLEtBQUssQ0FBQ0MsSUFBSSxDQUFFRixFQUFHLENBQUM7RUFDM0I7O0VBRUE7QUFDRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7RUFDQyxJQUFNRyxHQUFHLEdBQUc7SUFDWDtBQUNGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtJQUNFQyxRQUFRLFdBQVJBLFFBQVFBLENBQUVDLE9BQU8sRUFBRztNQUNuQk4sT0FBTyxDQUFFOUMsUUFBUSxDQUFDcUQsZ0JBQWdCLENBQUVELE9BQU8sR0FBRyxvQ0FBcUMsQ0FBRSxDQUFDLENBQ3BGRSxHQUFHLENBQ0gsVUFBVTdDLENBQUMsRUFBRztRQUFFO1FBQ2YsSUFBTWhCLEtBQUssR0FBRzhELFFBQVEsQ0FBRTlDLENBQUMsQ0FBQytDLE9BQU8sQ0FBQ0MsU0FBUyxFQUFFLEVBQUcsQ0FBQyxJQUFJLENBQUM7UUFFdERoRCxDQUFDLENBQUNJLEtBQUssR0FBR0osQ0FBQyxDQUFDSSxLQUFLLENBQUNtQyxLQUFLLENBQUUsQ0FBQyxFQUFFdkQsS0FBTSxDQUFDO1FBRW5DLElBQU1NLElBQUksR0FBR0osVUFBVSxDQUN0QmMsQ0FBQyxDQUFDK0MsT0FBTyxDQUFDNUQsTUFBTSxFQUNoQmEsQ0FBQyxDQUFDK0MsT0FBTyxDQUFDM0QsT0FBTyxFQUNqQlAsVUFBVSxDQUNUcUIsZ0JBQWdCLENBQUNDLG9CQUFvQixFQUNyQ0gsQ0FBQyxDQUFDSSxLQUFLLENBQUNDLE1BQU0sRUFDZHJCLEtBQ0QsQ0FDRCxDQUFDO1FBRUQsSUFBTWlFLEVBQUUsR0FBR2xELGVBQWUsQ0FBRVQsSUFBSSxFQUFFTixLQUFNLENBQUM7UUFFekNnQixDQUFDLENBQUNrRCxVQUFVLENBQUNDLFdBQVcsQ0FBRTdELElBQUssQ0FBQztRQUNoQ1UsQ0FBQyxDQUFDb0QsZ0JBQWdCLENBQUUsU0FBUyxFQUFFSCxFQUFHLENBQUM7UUFDbkNqRCxDQUFDLENBQUNvRCxnQkFBZ0IsQ0FBRSxPQUFPLEVBQUVILEVBQUcsQ0FBQztRQUNqQ2pELENBQUMsQ0FBQ29ELGdCQUFnQixDQUFFLE9BQU8sRUFBRS9CLFNBQVMsQ0FBRXJDLEtBQU0sQ0FBRSxDQUFDO01BQ2xELENBQ0QsQ0FBQztNQUVGcUQsT0FBTyxDQUFFOUMsUUFBUSxDQUFDcUQsZ0JBQWdCLENBQUVELE9BQU8sR0FBRywrQkFBZ0MsQ0FBRSxDQUFDLENBQy9FRSxHQUFHLENBQ0gsVUFBVTdDLENBQUMsRUFBRztRQUFFO1FBQ2YsSUFBTWhCLEtBQUssR0FBRzhELFFBQVEsQ0FBRTlDLENBQUMsQ0FBQytDLE9BQU8sQ0FBQ0MsU0FBUyxFQUFFLEVBQUcsQ0FBQyxJQUFJLENBQUM7UUFFdERoRCxDQUFDLENBQUNJLEtBQUssR0FBR3dCLFVBQVUsQ0FBRTVCLENBQUMsQ0FBQ0ksS0FBSyxFQUFFcEIsS0FBTSxDQUFDO1FBRXRDLElBQU1NLElBQUksR0FBR0osVUFBVSxDQUN0QmMsQ0FBQyxDQUFDK0MsT0FBTyxDQUFDNUQsTUFBTSxFQUNoQmEsQ0FBQyxDQUFDK0MsT0FBTyxDQUFDM0QsT0FBTyxFQUNqQlAsVUFBVSxDQUNUcUIsZ0JBQWdCLENBQUNZLGVBQWUsRUFDaENSLFVBQVUsQ0FBRU4sQ0FBQyxDQUFDSSxLQUFLLENBQUNRLElBQUksQ0FBQyxDQUFFLENBQUMsRUFDNUI1QixLQUNELENBQ0QsQ0FBQztRQUVELElBQU1pRSxFQUFFLEdBQUd0QyxVQUFVLENBQUVyQixJQUFJLEVBQUVOLEtBQU0sQ0FBQztRQUVwQ2dCLENBQUMsQ0FBQ2tELFVBQVUsQ0FBQ0MsV0FBVyxDQUFFN0QsSUFBSyxDQUFDO1FBRWhDVSxDQUFDLENBQUNvRCxnQkFBZ0IsQ0FBRSxTQUFTLEVBQUVILEVBQUcsQ0FBQztRQUNuQ2pELENBQUMsQ0FBQ29ELGdCQUFnQixDQUFFLE9BQU8sRUFBRUgsRUFBRyxDQUFDO1FBQ2pDakQsQ0FBQyxDQUFDb0QsZ0JBQWdCLENBQUUsT0FBTyxFQUFFaEIsVUFBVSxDQUFFcEQsS0FBTSxDQUFFLENBQUM7TUFDbkQsQ0FDRCxDQUFDO0lBQ0g7RUFDRCxDQUFDOztFQUVEO0FBQ0Q7QUFDQTtBQUNBO0FBQ0E7RUFDQyxTQUFTcUUsS0FBS0EsQ0FBQSxFQUFHO0lBQ2hCO0lBQ0FwRCxNQUFNLENBQUNxRCxnQkFBZ0IsR0FBR2IsR0FBRztJQUU3QkEsR0FBRyxDQUFDQyxRQUFRLENBQUUsTUFBTyxDQUFDO0VBQ3ZCO0VBRUEsSUFBS25ELFFBQVEsQ0FBQ2dFLFVBQVUsS0FBSyxTQUFTLEVBQUc7SUFDeENoRSxRQUFRLENBQUM2RCxnQkFBZ0IsQ0FBRSxrQkFBa0IsRUFBRUMsS0FBTSxDQUFDO0VBQ3ZELENBQUMsTUFBTTtJQUNOQSxLQUFLLENBQUMsQ0FBQztFQUNSO0FBQ0QsQ0FBQyxFQUFDLENBQUMiLCJpZ25vcmVMaXN0IjpbXX0= },{}]},{},[1])frontend/fields/text-limit.es5.min.js000064400000006232151716470030013553 0ustar00!function i(r,a,o){function s(n,t){if(!a[n]){if(!r[n]){var e="function"==typeof require&&require;if(!t&&e)return e(n,!0);if(l)return l(n,!0);throw new Error("Cannot find module '"+n+"'")}t=a[n]={exports:{}};r[n][0].call(t.exports,function(t){var e=r[n][1][t];return s(e||t)},t,t.exports,i,r,a,o)}return a[n].exports}for(var l="function"==typeof require&&require,t=0;t -1 && words >= limit ) { e.preventDefault(); } }; } /** * Get passed text from the clipboard. * * @since 1.5.6 * * @param {ClipboardEvent} e Clipboard event. * * @return {string} Text from clipboard. */ function getPastedText( e ) { if ( window.clipboardData && window.clipboardData.getData ) { // IE return window.clipboardData.getData( 'Text' ); } else if ( e.clipboardData && e.clipboardData.getData ) { return e.clipboardData.getData( 'text/plain' ); } return ''; } /** * Paste event higher order function for character limit. * * @since 1.6.7.1 * * @param {number} limit Max allowed number of characters. * * @return {Function} Event handler. */ function pasteText( limit ) { return function( e ) { e.preventDefault(); const pastedText = getPastedText( e ), newPosition = this.selectionStart + pastedText.length, newText = this.value.substring( 0, this.selectionStart ) + pastedText + this.value.substring( this.selectionStart ); this.value = newText.substring( 0, limit ); this.setSelectionRange( newPosition, newPosition ); }; } /** * Limit string length to a certain number of words, preserving line breaks. * * @since 1.6.8 * * @param {string} text Text. * @param {number} limit Max allowed number of words. * * @return {string} Text with the limited number of words. */ function limitWords( text, limit ) { let result = ''; // Regular expression pattern: match any space character. const regEx = /\s+/g; // Store separators for further join. const separators = text.trim().match( regEx ) || []; // Split the new text by regular expression. const newTextArray = text.split( regEx ); // Limit the number of words. newTextArray.splice( limit, newTextArray.length ); // Join the words together using stored separators. for ( let i = 0; i < newTextArray.length; i++ ) { result += newTextArray[ i ] + ( separators[ i ] || '' ); } return result.trim(); } /** * Paste event higher order function for words limit. * * @since 1.5.6 * * @param {number} limit Max allowed number of words. * * @return {Function} Event handler. */ function pasteWords( limit ) { return function( e ) { e.preventDefault(); const pastedText = getPastedText( e ), newPosition = this.selectionStart + pastedText.length, newText = this.value.substring( 0, this.selectionStart ) + pastedText + this.value.substring( this.selectionStart ); this.value = limitWords( newText, limit ); this.setSelectionRange( newPosition, newPosition ); }; } /** * Array.from polyfill. * * @since 1.5.6 * * @param {Object} el Iterator. * * @return {Object} Array. */ function arrFrom( el ) { return [].slice.call( el ); } /** * Public functions and properties. * * @since 1.8.9 * * @type {Object} */ const app = { /** * Init text limit hint. * * @since 1.8.9 * * @param {string} context Context selector. */ initHint( context ) { arrFrom( document.querySelectorAll( context + ' .wpforms-limit-characters-enabled' ) ) .map( function( e ) { // eslint-disable-line array-callback-return const limit = parseInt( e.dataset.textLimit, 10 ) || 0; e.value = e.value.slice( 0, limit ); const hint = createHint( e.dataset.formId, e.dataset.fieldId, renderHint( wpforms_settings.val_limit_characters, e.value.length, limit ) ); const fn = checkCharacters( hint, limit ); e.parentNode.appendChild( hint ); e.addEventListener( 'keydown', fn ); e.addEventListener( 'keyup', fn ); e.addEventListener( 'paste', pasteText( limit ) ); } ); arrFrom( document.querySelectorAll( context + ' .wpforms-limit-words-enabled' ) ) .map( function( e ) { // eslint-disable-line array-callback-return const limit = parseInt( e.dataset.textLimit, 10 ) || 0; e.value = limitWords( e.value, limit ); const hint = createHint( e.dataset.formId, e.dataset.fieldId, renderHint( wpforms_settings.val_limit_words, countWords( e.value.trim() ), limit ) ); const fn = checkWords( hint, limit ); e.parentNode.appendChild( hint ); e.addEventListener( 'keydown', fn ); e.addEventListener( 'keyup', fn ); e.addEventListener( 'paste', pasteWords( limit ) ); } ); }, }; /** * DOMContentLoaded handler. * * @since 1.5.6 */ function ready() { // Expose to the world. window.WPFormsTextLimit = app; app.initHint( 'body' ); } if ( document.readyState === 'loading' ) { document.addEventListener( 'DOMContentLoaded', ready ); } else { ready(); } }() ); frontend/wpforms-confirmation.js000064400000001132151716470030013105 0ustar00// Clear URL - remove wpforms_form_id ( function() { var loc = window.location, query = loc.search; if ( query.indexOf( 'wpforms_form_id=' ) !== -1 ) { query = query.replace( /([&?]wpforms_form_id=[0-9]*$|wpforms_form_id=[0-9]*&|[?&]wpforms_form_id=[0-9]*(?=#))/, '' ); history.replaceState( {}, null, loc.origin + loc.pathname + query ); } }() ); ( function( $ ) { $( function() { if ( $( 'div.wpforms-confirmation-scroll' ).length ) { $( 'html,body' ).animate( { scrollTop: ( $( 'div.wpforms-confirmation-scroll' ).offset().top ) - 100 }, 1000 ); } } ); }( jQuery ) ); frontend/wpforms-confirmation.min.js000064400000000641151716470030013673 0ustar00!function(){var o=window.location,r=o.search;-1!==r.indexOf("wpforms_form_id=")&&(r=r.replace(/([&?]wpforms_form_id=[0-9]*$|wpforms_form_id=[0-9]*&|[?&]wpforms_form_id=[0-9]*(?=#))/,""),history.replaceState({},null,o.origin+o.pathname+r))}(),function(o){o(function(){o("div.wpforms-confirmation-scroll").length&&o("html,body").animate({scrollTop:o("div.wpforms-confirmation-scroll").offset().top-100},1e3)})}(jQuery);frontend/wpforms-modern.js000064400000034726151716470030011720 0ustar00/* global wpforms_settings, WPFormsUtils */ /** * @param wpforms_settings.css_vars * @param wpforms_settings.formErrorMessagePrefix * @param wpforms_settings.indicatorStepsPattern * @param wpforms_settings.submitBtnDisabled */ // noinspection ES6ConvertVarToLetConst /** * Modern Frontend. * * @since 1.8.1 */ // eslint-disable-next-line no-var var WPForms = window.WPForms || {}; WPForms.FrontendModern = WPForms.FrontendModern || ( function( document, window, $ ) { // noinspection JSUnusedLocalSymbols,JSUnusedGlobalSymbols const app = { /** * Start the engine. * * @since 1.8.1 */ init() { // Document ready. $( app.ready ); app.bindOptinMonster(); }, /** * Document ready. * * @since 1.8.1 */ ready() { app.updateGBBlockAccentColors(); app.initPageBreakButtons(); app.initButtonStyle(); app.events(); }, /** * Events. * * @since 1.8.1 */ events() { $( document ) .on( 'wpforms_elementor_form_fields_initialized', app.initPageBreakButtons ); $( 'form.wpforms-form' ) .on( 'wpformsCombinedUploadsSizeError', app.combinedUploadsSizeError ) .on( 'wpformsFormSubmitButtonDisable', app.formSubmitButtonDisable ) .on( 'wpformsFormSubmitButtonRestore', app.formSubmitButtonRestore ) .on( 'wpformsPageChange', app.pageChange ); $( 'form.wpforms-form .wpforms-submit' ) .on( 'keydown click', app.disabledButtonPress ); // Add styling to timepicker dropdown. $( document ) .on( 'focus', '.wpforms-render-modern .wpforms-timepicker', app.updateTimepickerDropdown ); // Reset timepicker dropdown styles. $( document ) .on( 'focusout', '.wpforms-render-modern .wpforms-timepicker', app.resetTimepickerDropdown ); }, /** * OptinMonster compatibility. * * Re-initialize after OptinMonster loads to accommodate changes that * have occurred to the DOM. * * @since 1.9.0 */ bindOptinMonster() { // OM v5. document.addEventListener( 'om.Campaign.load', function() { app.ready(); } ); // OM Legacy. $( document ).on( 'OptinMonsterOnShow', function() { app.ready(); } ); }, /** * Add styling to timepicker dropdown. * * @since 1.8.8 */ updateTimepickerDropdown() { const cssVars = app.getCssVars( $( this ) ); setTimeout( function() { const $list = $( '.ui-timepicker-wrapper .ui-timepicker-list' ); $list.css( 'background', cssVars[ 'field-menu-color' ] ); $list.find( 'li' ).css( 'color', cssVars[ 'field-text-color' ] ); $list.find( '.ui-timepicker-selected' ) .css( 'background', cssVars[ 'button-background-color' ] ) .css( 'color', cssVars[ 'button-text-color' ] ); }, 0 ); }, /** * Reset timepicker dropdown styles. * * @since 1.8.9.5 */ resetTimepickerDropdown() { setTimeout( function() { const $list = $( '.ui-timepicker-wrapper .ui-timepicker-list' ); $list.find( ':not(.ui-timepicker-selected)' ).attr( 'style', '' ); }, 0 ); }, /** * Update accent colors of some fields in GB block in Modern Markup mode. * * @since 1.8.8 */ initButtonStyle() { // Loop through all the GB blocks on the page. $( '.wpforms-block.wpforms-container-full, .elementor-widget-wpforms .wpforms-container-full' ).each( function() { const $form = $( this ); const contStyle = getComputedStyle( $form.get( 0 ) ); const btnBgColor = app.getCssVar( contStyle, '--wpforms-button-background-color-alt' ); if ( app.isTransparentColor( btnBgColor ) ) { $form.find( 'button.wpforms-submit' ).addClass( 'wpforms-opacity-hover' ); } } ); }, /** * Checks if the provided color has transparency. * * @since 1.8.8 * * @param {string} color The color to check. * * @return {boolean} Returns true if the color is transparent. */ isTransparentColor( color ) { const rgba = app.getColorAsRGBArray( color ); // The max opacity value of the color that is considered as transparent. const opacityThreshold = 0.33; const opacity = Number( rgba?.[ 3 ] ); // Compare the opacity value with the threshold. return opacity <= opacityThreshold; }, /** * Update accent colors of some fields in GB block in Modern Markup mode. * * @since 1.8.1 */ updateGBBlockAccentColors() { // Loop through all the GB blocks on the page. $( '.wpforms-block.wpforms-container-full, .elementor-widget-wpforms .wpforms-container-full' ).each( function() { const $form = $( this ); app.updateGBBlockPageIndicatorColor( $form ); app.updateGBBlockIconChoicesColor( $form ); app.updateGBBlockRatingColor( $form ); } ); }, /** * Update accent color of Page Indicator. * * @since 1.8.1 * * @param {jQuery} $form Form container. */ updateGBBlockPageIndicatorColor( $form ) { const $indicator = $form.find( '.wpforms-page-indicator' ), $indicatorPage = $indicator.find( '.wpforms-page-indicator-page-progress, .wpforms-page-indicator-page.active .wpforms-page-indicator-page-number' ), $indicatorTriangle = $indicatorPage.find( '.wpforms-page-indicator-page-triangle' ); $indicator.data( 'indicator-color', 'var( --wpforms-page-break-color )' ); $indicatorPage.css( 'background-color', 'var( --wpforms-page-break-color )' ); $indicatorTriangle.css( 'border-top-color', 'var( --wpforms-page-break-color )' ); }, /** * Update accent color of Icon Choices. * * @since 1.8.1 * * @param {jQuery} $form Form container. */ updateGBBlockIconChoicesColor( $form ) { $form .find( '.wpforms-icon-choices' ) .css( '--wpforms-icon-choices-color', 'var( --wpforms-button-background-color )' ); }, /** * Update accent color of Rating field. * * @since 1.8.1 * * @param {jQuery} $form Form container. */ updateGBBlockRatingColor( $form ) { $form .find( '.wpforms-field-rating-item svg' ) .css( 'color', 'var( --wpforms-page-break-color, var( --wpforms-button-background-color ) )' ); }, /** * Init Page Break fields. * * @since 1.8.1 */ initPageBreakButtons() { $( '.wpforms-page-button' ) .removeClass( 'wpforms-disabled' ) .attr( 'aria-disabled', 'false' ) .attr( 'aria-describedby', '' ); }, /** * Handler for `wpformsCombinedUploadsSizeError` event. * Accessibility enhancements to error container and submit button. * * @since 1.8.1 * * @param {Object} e Event object. * @param {jQuery} $form Form object. * @param {jQuery} $errorCnt Error container object. */ combinedUploadsSizeError( e, $form, $errorCnt ) { const formId = $form.data( 'formid' ), errormessage = $form.attr( 'aria-errormessage' ) || '', errorCntId = `wpforms-${ formId }-footer-error`, $submitBtn = $form.find( '.wpforms-submit' ); $form.attr( { 'aria-invalid': 'true', 'aria-errormessage': `${ errormessage } ${ errorCntId }`, } ); $errorCnt.attr( { role: 'alert', id: errorCntId, } ); // Add error message prefix. $errorCnt.find( '> .wpforms-hidden:first-child' ).remove(); $errorCnt.prepend( `${ wpforms_settings.formErrorMessagePrefix }` ); // Instead of set the `disabled` property, // we must use `aria-disabled` and `aria-describedby` attributes in conduction with `wpforms-disabled` class. $submitBtn.attr( 'aria-describedby', errorCntId ); }, /** * Handler for `wpformsCombinedUploadsSizeOk` event. * * @since 1.8.1 * @deprecated 1.8.3 * * @param {Object} e Event object. * @param {jQuery} $form Form object. * @param {jQuery} $errorCnt Error container object. */ // eslint-disable-next-line no-unused-vars combinedUploadsSizeOk( e, $form, $errorCnt ) { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "WPForms.FrontendModern( e, $form, $errorCnt )" has been deprecated, please use the new "formSubmitButtonDisable: function( e, $form, $submitBtn )" function instead!' ); const $submitBtn = $form.find( '.wpforms-submit' ); // Revert aria-* attributes to the normal state. $submitBtn .removeClass( 'wpforms-disabled' ) .attr( 'aria-disabled', 'false' ) .attr( 'aria-describedby', '' ); }, /** * Handler for `wpformsFormSubmitButtonDisable` event. * * @since 1.8.1 * * @param {Object} e Event object. * @param {jQuery} $form Form object. * @param {jQuery} $submitBtn Submit a button object. */ formSubmitButtonDisable( e, $form, $submitBtn ) { const disabledBtnDescId = $form.attr( 'id' ) + '-submit-btn-disabled'; $submitBtn.before( `
    ${ wpforms_settings.submitBtnDisabled }
    ` ); $submitBtn .prop( 'disabled', false ) .addClass( 'wpforms-disabled' ) .attr( 'aria-disabled', 'true' ) .attr( 'aria-describedby', disabledBtnDescId ); }, /** * Handler for `wpformsFormSubmitButtonRestore` event. * * @since 1.8.1 * * @param {Object} e Event object. * @param {jQuery} $form Form object. * @param {jQuery} $submitBtn Submit a button object. */ formSubmitButtonRestore( e, $form, $submitBtn ) { const disabledBtnDescId = $form.attr( 'id' ) + '-submit-btn-disabled'; $form.find( '#' + disabledBtnDescId ).remove(); $submitBtn .removeClass( 'wpforms-disabled' ) .attr( 'aria-disabled', 'false' ) .attr( 'aria-describedby', '' ); }, /** * Disabled button click/keydown event handler. * * @since 1.8.1 * * @param {Object} e Event object. */ disabledButtonPress( e ) { const $submitBtn = $( this ); if ( ! $submitBtn.hasClass( 'wpforms-disabled' ) ) { return; } if ( e.key === 'Enter' || e.type === 'click' ) { e.preventDefault(); e.stopPropagation(); } }, /** * Page change event handler. * * @since 1.8.1 * * @param {Object} e Event object. * @param {number} nextPage The next page number. * @param {jQuery} $form Current form. */ pageChange( e, nextPage, $form ) { const $pageIndicator = $form.find( '.wpforms-page-indicator' ); if ( ! wpforms_settings.indicatorStepsPattern || ! $pageIndicator.length ) { return; } const totalPages = $form.find( '.wpforms-page' ).length; let msg = wpforms_settings.indicatorStepsPattern; let pageTitle; msg = msg.replace( '{current}', nextPage ).replace( '{total}', totalPages ); if ( $pageIndicator.hasClass( 'progress' ) ) { pageTitle = $pageIndicator.find( '.wpforms-page-indicator-page-title' ).data( `page-${ nextPage }-title` ); } else { pageTitle = $pageIndicator.find( `.wpforms-page-indicator-page-${ nextPage } .wpforms-page-indicator-page-title` ).text(); } msg = pageTitle ? pageTitle + '. ' + msg : msg; $pageIndicator.attr( 'aria-valuenow', nextPage ); app.screenReaderAnnounce( msg, 'polite' ); }, /** * Allows the screen reader to talk directly through the use of JS. * * @since 1.8.1 * * @param {string} text The message to be vocalised * @param {string} priority Aria-live priority. "polite" (by default) or "assertive". */ screenReaderAnnounce( text, priority ) { const el = document.createElement( 'div' ); const id = 'wpforms-screen-reader-announce-' + Date.now(); el.setAttribute( 'id', id ); el.setAttribute( 'aria-live', priority || 'polite' ); el.classList.add( 'wpforms-screen-reader-announce' ); const node = document.body.appendChild( el ); setTimeout( function() { node.innerHTML = text; }, 100 ); setTimeout( function() { document.body.removeChild( node ); }, 1000 ); }, /** * Add opacity to color string. * Supports formats: RGB, RGBA, HEX, HEXA. * * If the given color has an alpha channel, the new alpha channel will be calculated according to the given opacity. * * @since 1.8.1 * * @param {string} color Color. * @param {string} opacity Opacity. * * @return {string} Color in RGBA format with an added alpha channel according to given opacity. */ getColorWithOpacity( color, opacity ) { // Moved to ../share/utils.js return WPFormsUtils.cssColorsUtils.getColorWithOpacity( color, opacity ); }, /** * Remove opacity from the color value. * Supports formats: RGB, RGBA, HEX, HEXA. * * @since 1.8.1 * * @param {string} color Color. * * @return {string} Color in RGB format. */ getSolidColor( color ) { color = color.trim(); const rgbArray = app.getColorAsRGBArray( color ); if ( ! rgbArray ) { return color; } // Combine and return the RGB color. return `rgb(${ rgbArray[ 0 ] },${ rgbArray[ 1 ] },${ rgbArray[ 2 ] })`; }, /** * Check if the given color is a valid CSS color. * * @since 1.8.1 * * @param {string} color Color. * * @return {boolean} True if the given color is a valid CSS color. */ isValidColor( color ) { // Moved to ../share/utils.js return WPFormsUtils.cssColorsUtils.isValidColor( color ); }, /** * Get color as an array of RGB(A) values. * * @since 1.8.1 * * @param {string} color Color. * * @return {Array|boolean} Color as an array of RGBA values. False on error. */ getColorAsRGBArray( color ) { // Moved to ../share/utils.js return WPFormsUtils.cssColorsUtils.getColorAsRGBArray( color ); }, /** * Get CSS variable value. * * @since 1.8.1 * * @param {Object} style Computed style object. * @param {string} varName Style custom property name. * * @return {string|null} CSS variable value; */ getCssVar( style, varName ) { if ( ! style || typeof style.getPropertyValue !== 'function' ) { return null; } let value = style.getPropertyValue( varName ).trim(); if ( varName.includes( 'color' ) ) { value = value.replace( /\s/g, '' ); } return value; }, /** * Get all CSS variables. * * @since 1.8.1 * * @param {jQuery} $form Form OR any element inside the form. * * @return {Object} CSS variables; */ getCssVars( $form ) { if ( ! $form || ! $form.length ) { return null; } const $cont = $form.hasClass( 'wpforms-container' ) ? $form : $form.closest( '.wpforms-container' ); const contStyle = getComputedStyle( $cont.get( 0 ) ); const cssVars = wpforms_settings.css_vars; const vars = {}; for ( let i = 0; i < cssVars.length; i++ ) { vars[ cssVars[ i ] ] = app.getCssVar( contStyle, '--wpforms-' + cssVars[ i ] ); } return vars; }, }; return app; }( document, window, jQuery ) ); // Initialize. WPForms.FrontendModern.init(); frontend/wpforms-modern.min.js000064400000013566151716470030012501 0ustar00var WPForms=window.WPForms||{};WPForms.FrontendModern=WPForms.FrontendModern||function(i,o){const a={init(){o(a.ready),a.bindOptinMonster()},ready(){a.updateGBBlockAccentColors(),a.initPageBreakButtons(),a.initButtonStyle(),a.events()},events(){o(i).on("wpforms_elementor_form_fields_initialized",a.initPageBreakButtons),o("form.wpforms-form").on("wpformsCombinedUploadsSizeError",a.combinedUploadsSizeError).on("wpformsFormSubmitButtonDisable",a.formSubmitButtonDisable).on("wpformsFormSubmitButtonRestore",a.formSubmitButtonRestore).on("wpformsPageChange",a.pageChange),o("form.wpforms-form .wpforms-submit").on("keydown click",a.disabledButtonPress),o(i).on("focus",".wpforms-render-modern .wpforms-timepicker",a.updateTimepickerDropdown),o(i).on("focusout",".wpforms-render-modern .wpforms-timepicker",a.resetTimepickerDropdown)},bindOptinMonster(){i.addEventListener("om.Campaign.load",function(){a.ready()}),o(i).on("OptinMonsterOnShow",function(){a.ready()})},updateTimepickerDropdown(){const e=a.getCssVars(o(this));setTimeout(function(){var r=o(".ui-timepicker-wrapper .ui-timepicker-list");r.css("background",e["field-menu-color"]),r.find("li").css("color",e["field-text-color"]),r.find(".ui-timepicker-selected").css("background",e["button-background-color"]).css("color",e["button-text-color"])},0)},resetTimepickerDropdown(){setTimeout(function(){o(".ui-timepicker-wrapper .ui-timepicker-list").find(":not(.ui-timepicker-selected)").attr("style","")},0)},initButtonStyle(){o(".wpforms-block.wpforms-container-full, .elementor-widget-wpforms .wpforms-container-full").each(function(){var r=o(this),e=getComputedStyle(r.get(0)),e=a.getCssVar(e,"--wpforms-button-background-color-alt");a.isTransparentColor(e)&&r.find("button.wpforms-submit").addClass("wpforms-opacity-hover")})},isTransparentColor(r){r=a.getColorAsRGBArray(r);return Number(r?.[3])<=.33},updateGBBlockAccentColors(){o(".wpforms-block.wpforms-container-full, .elementor-widget-wpforms .wpforms-container-full").each(function(){var r=o(this);a.updateGBBlockPageIndicatorColor(r),a.updateGBBlockIconChoicesColor(r),a.updateGBBlockRatingColor(r)})},updateGBBlockPageIndicatorColor(r){var r=r.find(".wpforms-page-indicator"),e=r.find(".wpforms-page-indicator-page-progress, .wpforms-page-indicator-page.active .wpforms-page-indicator-page-number"),o=e.find(".wpforms-page-indicator-page-triangle");r.data("indicator-color","var( --wpforms-page-break-color )"),e.css("background-color","var( --wpforms-page-break-color )"),o.css("border-top-color","var( --wpforms-page-break-color )")},updateGBBlockIconChoicesColor(r){r.find(".wpforms-icon-choices").css("--wpforms-icon-choices-color","var( --wpforms-button-background-color )")},updateGBBlockRatingColor(r){r.find(".wpforms-field-rating-item svg").css("color","var( --wpforms-page-break-color, var( --wpforms-button-background-color ) )")},initPageBreakButtons(){o(".wpforms-page-button").removeClass("wpforms-disabled").attr("aria-disabled","false").attr("aria-describedby","")},combinedUploadsSizeError(r,e,o){var t=e.data("formid"),s=e.attr("aria-errormessage")||"",t=`wpforms-${t}-footer-error`,i=e.find(".wpforms-submit");e.attr({"aria-invalid":"true","aria-errormessage":s+" "+t}),o.attr({role:"alert",id:t}),o.find("> .wpforms-hidden:first-child").remove(),o.prepend(`${wpforms_settings.formErrorMessagePrefix}`),i.attr("aria-describedby",t)},combinedUploadsSizeOk(r,e,o){console.warn('WARNING! Function "WPForms.FrontendModern( e, $form, $errorCnt )" has been deprecated, please use the new "formSubmitButtonDisable: function( e, $form, $submitBtn )" function instead!'),e.find(".wpforms-submit").removeClass("wpforms-disabled").attr("aria-disabled","false").attr("aria-describedby","")},formSubmitButtonDisable(r,e,o){e=e.attr("id")+"-submit-btn-disabled";o.before(`
    ${wpforms_settings.submitBtnDisabled}
    `),o.prop("disabled",!1).addClass("wpforms-disabled").attr("aria-disabled","true").attr("aria-describedby",e)},formSubmitButtonRestore(r,e,o){var t=e.attr("id")+"-submit-btn-disabled";e.find("#"+t).remove(),o.removeClass("wpforms-disabled").attr("aria-disabled","false").attr("aria-describedby","")},disabledButtonPress(r){!o(this).hasClass("wpforms-disabled")||"Enter"!==r.key&&"click"!==r.type||(r.preventDefault(),r.stopPropagation())},pageChange(r,o,t){var s=t.find(".wpforms-page-indicator");if(wpforms_settings.indicatorStepsPattern&&s.length){t=t.find(".wpforms-page").length;let r=wpforms_settings.indicatorStepsPattern,e;r=r.replace("{current}",o).replace("{total}",t),e=s.hasClass("progress")?s.find(".wpforms-page-indicator-page-title").data(`page-${o}-title`):s.find(`.wpforms-page-indicator-page-${o} .wpforms-page-indicator-page-title`).text(),r=e?e+". "+r:r,s.attr("aria-valuenow",o),a.screenReaderAnnounce(r,"polite")}},screenReaderAnnounce(r,e){var o=i.createElement("div"),t="wpforms-screen-reader-announce-"+Date.now();o.setAttribute("id",t),o.setAttribute("aria-live",e||"polite"),o.classList.add("wpforms-screen-reader-announce");const s=i.body.appendChild(o);setTimeout(function(){s.innerHTML=r},100),setTimeout(function(){i.body.removeChild(s)},1e3)},getColorWithOpacity(r,e){return WPFormsUtils.cssColorsUtils.getColorWithOpacity(r,e)},getSolidColor(r){r=r.trim();var e=a.getColorAsRGBArray(r);return e?`rgb(${e[0]},${e[1]},${e[2]})`:r},isValidColor(r){return WPFormsUtils.cssColorsUtils.isValidColor(r)},getColorAsRGBArray(r){return WPFormsUtils.cssColorsUtils.getColorAsRGBArray(r)},getCssVar(r,e){if(!r||"function"!=typeof r.getPropertyValue)return null;let o=r.getPropertyValue(e).trim();return o=e.includes("color")?o.replace(/\s/g,""):o},getCssVars(r){if(!r||!r.length)return null;var r=r.hasClass("wpforms-container")?r:r.closest(".wpforms-container"),e=getComputedStyle(r.get(0)),o=wpforms_settings.css_vars,t={};for(let r=0;r
    `; $insertBeforeField.before( fieldHTML ); // Add inline properties for honeypot field on the form. const $fieldContainer = $( `#wpforms-${ formId }-field_${ wpforms_settings.hn_data[ formId ] }-container`, $form ); $fieldContainer.find( 'input' ).attr( { tabindex: '-1', 'aria-hidden': 'true', } ); $fieldContainer.find( 'label' ).text( label ).attr( 'aria-hidden', 'true' ); } ); }, /** * Generate random Honeypot label. * * @since 1.9.0 * * @param {Array} words List of words. * * @return {string} Honeypot label. */ getHoneypotRandomLabel( words ) { let label = ''; for ( let i = 0; i < 3; i++ ) { label += words[ Math.floor( Math.random() * words.length ) ] + ' '; } return label.trim(); }, /** * Get Honeypot field ID. * * @since 1.9.0 * * @param {Array} fieldIds List of the form field IDs. * * @return {number} Honeypot field ID. */ getHoneypotFieldId( fieldIds ) { const maxId = Math.max( ...fieldIds ); let honeypotFieldId = 0; // Find the first available field ID. for ( let i = 1; i < maxId; i++ ) { if ( ! fieldIds.includes( i ) ) { honeypotFieldId = i; break; } } // If no available field ID found, use the max ID + 1. if ( ! honeypotFieldId ) { honeypotFieldId = maxId + 1; } return honeypotFieldId; }, /** * Load jQuery Validation. * * @since 1.2.3 */ loadValidation() { // eslint-disable-line max-lines-per-function // Only load if jQuery validation library exists. if ( typeof $.fn.validate === 'undefined' ) { if ( window.location.hash && '#wpformsdebug' === window.location.hash ) { // eslint-disable-next-line no-console console.log( 'jQuery Validation library not found.' ); } return; } // jQuery Validation library will not correctly validate // fields that do not have a name attribute, so we use the // `wpforms-input-temp-name` class to add a temporary name // attribute before validation is initialized, then remove it // before the form submits. $( '.wpforms-input-temp-name' ).each( function( index, el ) { const random = Math.floor( Math.random() * 9999 ) + 1; $( this ).attr( 'name', 'wpf-temp-' + random ); } ); // Prepend URL field contents with https:// if user input doesn't contain a schema. $( document ).on( 'change', '.wpforms-validate input[type=url]', function() { const url = $( this ).val(); if ( ! url ) { return false; } if ( url.substr( 0, 7 ) !== 'http://' && url.substr( 0, 8 ) !== 'https://' ) { $( this ).val( 'https://' + url ); } } ); $.validator.messages.required = wpforms_settings.val_required; $.validator.messages.url = wpforms_settings.val_url; $.validator.messages.email = wpforms_settings.val_email; $.validator.messages.number = wpforms_settings.val_number; // Payments: Validate method for Credit Card Number. if ( typeof $.fn.payment !== 'undefined' ) { $.validator.addMethod( 'creditcard', function( value, element ) { //var type = $.payment.cardType(value); const valid = $.payment.validateCardNumber( value ); return this.optional( element ) || valid; }, wpforms_settings.val_creditcard ); // @todo validate CVC and expiration } // Validate method for file extensions. $.validator.addMethod( 'extension', function( value, element, param ) { param = 'string' === typeof param ? param.replace( /,/g, '|' ) : 'png|jpe?g|gif'; return this.optional( element ) || value.match( new RegExp( '\\.(' + param + ')$', 'i' ) ); }, wpforms_settings.val_fileextension ); // Validate method for file size. $.validator.addMethod( 'maxsize', function( value, element, param ) { const maxSize = param, optionalValue = this.optional( element ); let i, len, file; if ( optionalValue ) { return optionalValue; } if ( element.files && element.files.length ) { i = 0; len = element.files.length; for ( ; i < len; i++ ) { file = element.files[ i ]; if ( file.size > maxSize ) { return false; } } } return true; }, wpforms_settings.val_filesize ); $.validator.addMethod( 'step', function( value, element, param ) { const decimalPlaces = function( num ) { if ( Math.floor( num ) === num ) { return 0; } return num.toString().split( '.' )[ 1 ].length || 0; }; const decimals = decimalPlaces( param ); const decimalToInt = function( num ) { return Math.round( num * Math.pow( 10, decimals ) ); }; const min = decimalToInt( $( element ).attr( 'min' ) ); value = decimalToInt( value ) - min; return this.optional( element ) || decimalToInt( value ) % decimalToInt( param ) === 0; } ); // Validate email addresses. $.validator.methods.email = function( value, element ) { /** * This function combines is_email() from WordPress core * and wpforms_is_email() to validate email addresses. * * @see https://developer.wordpress.org/reference/functions/is_email/ * @see https://github.com/awesomemotive/wpforms-plugin/blob/develop/wpforms/includes/functions/checks.php#L45 * * @param {string} value The email address to validate. * * @return {boolean} True if the email address is valid, false otherwise. */ const isEmail = function( value ) { // eslint-disable-line complexity if ( typeof value !== 'string' ) { // Do not allow callables, arrays, and objects. return false; } // Check the length and position of the @ character. const atIndex = value.indexOf( '@', 1 ); if ( value.length < 6 || value.length > 254 || atIndex === -1 ) { return false; } // Check for more than one "@" symbol. if ( value.indexOf( '@', atIndex + 1 ) !== -1 ) { return false; } // Split email address into local and domain parts. const [ local, domain ] = value.split( '@' ); // Check local and domain parts for existence. if ( ! local || ! domain ) { return false; } // Check local part for invalid characters and length. const localRegex = /^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$/; if ( ! localRegex.test( local ) || local.length > 63 ) { return false; } // Check domain part for sequences of periods, leading and trailing periods, and whitespace. const domainRegex = /\.{2,}/; if ( domainRegex.test( domain ) || domain.trim( ' \t\n\r\0\x0B.' ) !== domain ) { return false; } // Check domain part for length. const domainArr = domain.split( '.' ); if ( domainArr.length < 2 ) { return false; } // Check domain label for length, leading and trailing periods, and whitespace. const domainLabelRegex = /^[a-z0-9-]+$/i; for ( const domainLabel of domainArr ) { if ( domainLabel.length > 63 || domainLabel.trim( ' \t\n\r\0\x0B-' ) !== domainLabel || ! domainLabelRegex.test( domainLabel ) ) { return false; } } return true; }; // Congratulations! The email address is valid. return this.optional( element ) || isEmail( value ); }; // Validate email by allowlist/blocklist. $.validator.addMethod( 'restricted-email', function( value, element ) { const $el = $( element ); if ( ! $el.val().length ) { return true; } const $form = $el.closest( '.wpforms-form' ), formId = $form.data( 'formid' ); if ( ! Object.prototype.hasOwnProperty.call( app.cache, formId ) || ! Object.prototype.hasOwnProperty.call( app.cache[ formId ], 'restrictedEmailValidation' ) || ! Object.prototype.hasOwnProperty.call( app.cache[ formId ].restrictedEmailValidation, value ) ) { app.restrictedEmailRequest( element, value ); return 'pending'; } return app.cache[ formId ].restrictedEmailValidation[ value ]; }, wpforms_settings.val_email_restricted ); // Validate confirmations. $.validator.addMethod( 'confirm', function( value, element, param ) { const field = $( element ).closest( '.wpforms-field' ); return $( field.find( 'input' )[ 0 ] ).val() === $( field.find( 'input' )[ 1 ] ).val(); }, wpforms_settings.val_confirm ); // Validate required payments. $.validator.addMethod( 'required-payment', function( value, element ) { return app.amountSanitize( value ) > 0; }, wpforms_settings.val_requiredpayment ); // Validate 12-hour time. $.validator.addMethod( 'time12h', function( value, element ) { // noinspection RegExpRedundantEscape return this.optional( element ) || /^((0?[1-9]|1[012])(:[0-5]\d){1,2}(\ ?[AP]M))$/i.test( value ); // eslint-disable-line no-useless-escape }, wpforms_settings.val_time12h ); // Validate 24-hour time. $.validator.addMethod( 'time24h', function( value, element ) { // noinspection RegExpRedundantEscape return this.optional( element ) || /^(([0-1]?[0-9])|([2][0-3])):([0-5]?[0-9])(\ ?[AP]M)?$/i.test( value ); // eslint-disable-line no-useless-escape }, wpforms_settings.val_time24h ); // Validate Turnstile captcha. $.validator.addMethod( 'turnstile', function( value ) { return value; }, wpforms_settings.val_turnstile_fail_msg ); // Validate time limits. $.validator.addMethod( 'time-limit', function( value, element ) { // eslint-disable-line complexity const $input = $( element ), minTime = $input.data( 'min-time' ), isLimited = typeof minTime !== 'undefined'; if ( ! isLimited ) { return true; } const isRequired = $input.prop( 'required' ); if ( ! isRequired && app.empty( value ) ) { return true; } const maxTime = $input.data( 'max-time' ); if ( app.compareTimesGreaterThan( maxTime, minTime ) ) { return app.compareTimesGreaterThan( value, minTime ) && app.compareTimesGreaterThan( maxTime, value ); } return ( app.compareTimesGreaterThan( value, minTime ) && app.compareTimesGreaterThan( value, maxTime ) ) || ( app.compareTimesGreaterThan( minTime, value ) && app.compareTimesGreaterThan( maxTime, value ) ); }, function( params, element ) { const $input = $( element ); let minTime = $input.data( 'min-time' ), maxTime = $input.data( 'max-time' ); // Replace `00:**pm` with `12:**pm`. minTime = minTime.replace( /^00:([0-9]{2})pm$/, '12:$1pm' ); maxTime = maxTime.replace( /^00:([0-9]{2})pm$/, '12:$1pm' ); // Proper format time: add space before AM/PM, make uppercase. minTime = minTime.replace( /(am|pm)/g, ' $1' ).toUpperCase(); maxTime = maxTime.replace( /(am|pm)/g, ' $1' ).toUpperCase(); return wpforms_settings.val_time_limit .replace( '{minTime}', minTime ) .replace( '{maxTime}', maxTime ); } ); // Validate checkbox choice limit. $.validator.addMethod( 'check-limit', function( value, element ) { const $ul = $( element ).closest( 'ul' ), choiceLimit = parseInt( $ul.attr( 'data-choice-limit' ) || 0, 10 ); if ( 0 === choiceLimit ) { return true; } const $checked = $ul.find( 'input[type="checkbox"]:checked' ); return $checked.length <= choiceLimit; }, function( params, element ) { const choiceLimit = parseInt( $( element ).closest( 'ul' ).attr( 'data-choice-limit' ) || 0, 10 ); return wpforms_settings.val_checklimit.replace( '{#}', choiceLimit ); } ); // Validate Inputmask completeness. $.validator.addMethod( 'inputmask-incomplete', function( value, element ) { if ( value.length === 0 || typeof $.fn.inputmask === 'undefined' ) { return true; } return $( element ).inputmask( 'isComplete' ); }, wpforms_settings.val_inputmask_incomplete ); // Validate Payment item value on zero. $.validator.addMethod( 'required-positive-number', function( value, element ) { return app.amountSanitize( value ) > 0; }, wpforms_settings.val_number_positive ); /** * Validate Payment item minimum price value. * * @since 1.8.6 */ $.validator.addMethod( 'required-minimum-price', function( value, element, param ) { const $el = $( element ); /** * The validation is passed in the following cases: * 1) if a field is not filled in and not required. * 2) if the minimum required price is equal to or less than the typed value. * Note: since the param is returned in decimal format at all times, we need to format the value to compare it. */ return ( value === '' && ! $el.hasClass( 'wpforms-field-required' ) ) || Number( app.amountSanitize( app.amountFormat( param ) ) ) <= Number( app.amountSanitize( value ) ); }, wpforms_settings.val_minimum_price ); // Validate password strength. $.validator.addMethod( 'password-strength', function( value, element ) { const $el = $( element ); // Need to check if the password strength to remove the error message. const strength = WPFormsPasswordField.passwordStrength( value, element ); /** * The validation is passed in the following cases: * 1) if a field is not filled in and not required. * 2) if the password strength is equal to or greater than the specified level. */ return ( value === '' && ! $el.hasClass( 'wpforms-field-required' ) ) || strength >= Number( $el.data( 'password-strength-level' ) ); }, wpforms_settings.val_password_strength ); // Finally, load jQuery Validation library for our forms. $( '.wpforms-validate' ).each( function() { // eslint-disable-line max-lines-per-function const form = $( this ), formID = form.data( 'formid' ); let properties; // TODO: cleanup this BC with wpforms_validate. if ( typeof window[ 'wpforms_' + formID ] !== 'undefined' && window[ 'wpforms_' + formID ].hasOwnProperty( 'validate' ) ) { properties = window[ 'wpforms_' + formID ].validate; } else if ( typeof wpforms_validate !== 'undefined' ) { properties = wpforms_validate; } else { properties = { errorElement: app.isModernMarkupEnabled() ? 'em' : 'label', errorClass: 'wpforms-error', validClass: 'wpforms-valid', ignore: ':hidden:not(textarea.wp-editor-area), .wpforms-conditional-hide textarea.wp-editor-area', ignoreTitle: true, errorPlacement( error, element ) { // eslint-disable-line complexity if ( app.isLikertScaleField( element ) ) { element.closest( 'table' ).hasClass( 'single-row' ) ? element.closest( '.wpforms-field' ).append( error ) : element.closest( 'tr' ).find( 'th' ).append( error ); } else if ( app.isWrappedField( element ) ) { element.closest( '.wpforms-field' ).append( error ); } else if ( app.isDateTimeField( element ) ) { app.dateTimeErrorPlacement( element, error ); } else if ( app.isFieldInColumn( element ) ) { element.parent().append( error ); } else if ( app.isFieldHasHint( element ) ) { element.parent().append( error ); } else if ( app.isLeadFormsSelect( element ) ) { element.parent().parent().append( error ); } else if ( element.hasClass( 'wp-editor-area' ) ) { element.parent().parent().parent().append( error ); } else { error.insertAfter( element ); } if ( app.isModernMarkupEnabled() ) { error.attr( { role: 'alert', 'aria-label': wpforms_settings.errorMessagePrefix, for: '', } ); } }, highlight( element, errorClass, validClass ) { // eslint-disable-line complexity const $element = $( element ), $field = $element.closest( '.wpforms-field' ), inputName = $element.attr( 'name' ); if ( 'radio' === $element.attr( 'type' ) || 'checkbox' === $element.attr( 'type' ) ) { $field.find( 'input[name="' + inputName + '"]' ).addClass( errorClass ).removeClass( validClass ); } else { $element.addClass( errorClass ).removeClass( validClass ); } // Remove password strength container for empty required password field. if ( $element.attr( 'type' ) === 'password' && $element.val().trim() === '' && window.WPFormsPasswordField && $element.data( 'rule-password-strength' ) && $element.hasClass( 'wpforms-field-required' ) ) { WPFormsPasswordField.passwordStrength( '', element ); } $field.addClass( 'wpforms-has-error' ); }, unhighlight( element, errorClass, validClass ) { const $element = $( element ), $field = $element.closest( '.wpforms-field' ), inputName = $element.attr( 'name' ); if ( 'radio' === $element.attr( 'type' ) || 'checkbox' === $element.attr( 'type' ) ) { $field.find( 'input[name="' + inputName + '"]' ).addClass( validClass ).removeClass( errorClass ); } else { $element.addClass( validClass ).removeClass( errorClass ); } // Remove the error class from the field container if there are no subfield errors. if ( ! $field.find( ':input.wpforms-error,[data-dz-errormessage]:not(:empty)' ).length ) { $field.removeClass( 'wpforms-has-error' ); } // Remove an error message to be sure the next time the `errorPlacement` method will be executed. if ( app.isModernMarkupEnabled() ) { $element.parent().find( 'em.wpforms-error' ).remove(); } }, submitHandler( form ) { /** * Captcha error handler. * * @since 1.8.4 * * @param {jQuery} $form current form element. * @param {jQuery} $container current form container. */ const captchaErrorDisplay = function( $form, $container ) { let errorTag = 'label', errorRole = ''; if ( app.isModernMarkupEnabled() ) { errorTag = 'em'; errorRole = 'role="alert"'; } const error = `<${ errorTag } id="wpforms-field_recaptcha-error" class="wpforms-error" ${ errorRole }> ${ wpforms_settings.val_recaptcha_fail_msg }`; $form.find( '.wpforms-recaptcha-container' ).append( error ); app.restoreSubmitButton( $form, $container ); }; const disableSubmitButton = function( $form ) { const $submit = $form.find( '.wpforms-submit' ); $submit.prop( 'disabled', true ); WPFormsUtils.triggerEvent( $form, 'wpformsFormSubmitButtonDisable', [ $form, $submit ] ); }; /** * Submit handler routine. * * @since 1.7.2 * * @return {boolean|void} False if form won't submit. */ const submitHandlerRoutine = function() { // eslint-disable-line complexity const $form = $( form ), $container = $form.closest( '.wpforms-container' ), $submit = $form.find( '.wpforms-submit' ), isCaptchaInvalid = $submit.data( 'captchaInvalid' ), altText = $submit.data( 'alt-text' ), recaptchaID = $submit.get( 0 ).recaptchaID; if ( $form.data( 'token' ) && 0 === $( '.wpforms-token', $form ).length ) { $( '' ) .val( $form.data( 'token' ) ) .appendTo( $form ); } $form.find( '#wpforms-field_recaptcha-error' ).remove(); disableSubmitButton( $form ); // Display processing text. if ( altText ) { $submit.text( altText ); } if ( isCaptchaInvalid ) { return captchaErrorDisplay( $form, $container ); } if ( ! app.empty( recaptchaID ) || recaptchaID === 0 ) { // The Form contains invisible reCAPTCHA. grecaptcha.execute( recaptchaID ).then( null, function() { if ( grecaptcha.getResponse() ) { return; } captchaErrorDisplay( $form, $container ); } ); return false; } // Remove name attributes if needed. $( '.wpforms-input-temp-name' ).removeAttr( 'name' ); app.formSubmit( $form ); }; // In the case of active Google reCAPTCHA v3, first, we should call `grecaptcha.execute`. // This is needed to get a proper grecaptcha token before submitting the form. if ( typeof wpformsRecaptchaV3Execute === 'function' ) { disableSubmitButton( $( form ) ); return wpformsRecaptchaV3Execute( submitHandlerRoutine ); } return submitHandlerRoutine(); }, invalidHandler( event, validator ) { if ( typeof validator.errorList[ 0 ] !== 'undefined' ) { app.scrollToError( $( validator.errorList[ 0 ].element ) ); } }, onkeyup: WPFormsUtils.debounce( // eslint-disable-next-line complexity function( element, event ) { // This code is copied from JQuery Validate 'onkeyup' method with only one change: 'wpforms-novalidate-onkeyup' class check. const excludedKeys = [ 16, 17, 18, 20, 35, 36, 37, 38, 39, 40, 45, 144, 225 ]; if ( $( element ).hasClass( 'wpforms-novalidate-onkeyup' ) ) { return; // Disable onkeyup validation for some elements (e.g. remote calls). } // eslint-disable-next-line no-mixed-operators if ( event.which === 9 && this.elementValue( element ) === '' || $.inArray( event.keyCode, excludedKeys ) !== -1 ) { } else if ( element.name in this.submitted || element.name in this.invalid ) { this.element( element ); } }, 1000 ), onfocusout: function( element ) { // eslint-disable-line complexity, object-shorthand // This code is copied from JQuery Validate 'onfocusout' method with only one change: 'wpforms-novalidate-onkeyup' class check. let validate = false; if ( $( element ).hasClass( 'wpforms-novalidate-onkeyup' ) && ! element.value ) { validate = true; // Empty value error handling for elements with onkeyup validation disabled. } if ( ! this.checkable( element ) && ( element.name in this.submitted || ! this.optional( element ) ) ) { validate = true; } // If the error comes from server validation, we don't need to validate it again, // because it will clean the error message too early. if ( $( element ).data( 'server-error' ) ) { validate = false; } if ( validate ) { this.element( element ); } }, onclick( element ) { let validate = false; const type = ( element || {} ).type; let $el = $( element ); if ( [ 'checkbox', 'radio' ].indexOf( type ) > -1 ) { if ( $el.hasClass( 'wpforms-likert-scale-option' ) ) { $el = $el.closest( 'tr' ); } else { $el = $el.closest( '.wpforms-field' ); } $el.find( 'label.wpforms-error, em.wpforms-error' ).remove(); validate = true; } if ( validate ) { this.element( element ); } }, }; } form.validate( properties ); app.loadValidationGroups( form ); } ); }, /** * Request to check if email is restricted. * * @since 1.8.5 * * @param {Element} element Email input field. * @param {string} value Field value. */ restrictedEmailRequest( element, value ) { const $el = $( element ); const $form = $el.closest( 'form' ); const validator = $form.data( 'validator' ); const formId = $form.data( 'formid' ); const $field = $el.closest( '.wpforms-field' ); const fieldId = $field.data( 'field-id' ); app.cache[ formId ] = app.cache[ formId ] || {}; validator.startRequest( element ); $.post( { url: wpforms_settings.ajaxurl, type: 'post', data: { action: 'wpforms_restricted_email', form_id: formId, // eslint-disable-line camelcase field_id: fieldId, // eslint-disable-line camelcase email: value, }, dataType: 'json', success( response ) { const errors = {}; const isValid = response.success && response.data; if ( ! isValid ) { errors[ element.name ] = wpforms_settings.val_email_restricted; validator.showErrors( errors ); } app.cache[ formId ].restrictedEmailValidation = app.cache[ formId ].restrictedEmailValidation || []; if ( ! Object.prototype.hasOwnProperty.call( app.cache[ formId ].restrictedEmailValidation, value ) ) { app.cache[ formId ].restrictedEmailValidation[ value ] = isValid; } validator.stopRequest( element, isValid ); }, } ); }, /** * Is field inside column. * * @since 1.6.3 * * @param {jQuery} element current form element. * * @return {boolean} true/false. */ isFieldInColumn( element ) { return element.parent().hasClass( 'wpforms-one-half' ) || element.parent().hasClass( 'wpforms-two-fifths' ) || element.parent().hasClass( 'wpforms-one-fifth' ); }, /** * Is field has hint (sublabel, description, limit text hint, etc.). * * @since 1.8.1 * * @param {jQuery} element current form element. * * @return {boolean} true/false. */ isFieldHasHint( element ) { return element .nextAll( '.wpforms-field-sublabel, .wpforms-field-description, .wpforms-field-limit-text, .wpforms-pass-strength-result' ) .length > 0; }, /** * Is datetime field. * * @since 1.6.3 * * @param {jQuery} element current form element. * * @return {boolean} true/false. */ isDateTimeField( element ) { return element.hasClass( 'wpforms-timepicker' ) || element.hasClass( 'wpforms-datepicker' ) || ( element.is( 'select' ) && element.attr( 'class' ).match( /date-month|date-day|date-year/ ) ); }, /** * Is a field wrapped in some container. * * @since 1.6.3 * * @param {jQuery} element current form element. * * @return {boolean} true/false. */ isWrappedField( element ) { // eslint-disable-line complexity return 'checkbox' === element.attr( 'type' ) || 'radio' === element.attr( 'type' ) || 'range' === element.attr( 'type' ) || 'select' === element.is( 'select' ) || 1 === element.data( 'is-wrapped-field' ) || element.parent().hasClass( 'iti' ) || element.hasClass( 'wpforms-validation-group-member' ) || element.hasClass( 'choicesjs-select' ) || element.hasClass( 'wpforms-net-promoter-score-option' ) || element.hasClass( 'wpforms-field-payment-coupon-input' ); }, /** * Is likert scale field. * * @since 1.6.3 * * @param {jQuery} element current form element. * * @return {boolean} true/false. */ isLikertScaleField( element ) { return element.hasClass( 'wpforms-likert-scale-option' ); }, /** * Is Lead Forms select field. * * @since 1.8.1 * * @param {jQuery} element current form element. * * @return {boolean} true/false. */ isLeadFormsSelect( element ) { return element.parent().hasClass( 'wpforms-lead-forms-select' ); }, /** * Is Coupon field. * * @since 1.8.2 * @deprecated 1.8.4 Deprecated. * * @param {jQuery} element current form element. * * @return {boolean} true/false. */ isCoupon( element ) { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "wpforms.isCoupon( element )" has been deprecated' ); return element.closest( '.wpforms-field' ).hasClass( 'wpforms-field-payment-coupon' ); }, /** * Print error message into date time fields. * * @since 1.6.3 * * @param {jQuery} element current form element. * @param {string} error Error message. */ dateTimeErrorPlacement( element, error ) { const $wrapper = element.closest( '.wpforms-field-row-block, .wpforms-field-date-time' ); if ( $wrapper.length ) { if ( ! $wrapper.find( 'label.wpforms-error, em.wpforms-error' ).length ) { $wrapper.append( error ); } } else { element.closest( '.wpforms-field' ).append( error ); } }, /** * Load jQuery Date Picker. * * @since 1.2.3 * @since 1.8.9 Added the `$context` parameter. * * @param {jQuery} $context Container to search for datepicker elements. */ loadDatePicker( $context ) { // eslint-disable-line max-lines-per-function // Only load if jQuery datepicker library exists. if ( typeof $.fn.flatpickr === 'undefined' ) { return; } $context = $context?.length ? $context : $( document ); $context.find( '.wpforms-datepicker-wrap' ).each( function() { // eslint-disable-line complexity, max-lines-per-function const element = $( this ), $input = element.find( 'input' ), form = element.closest( '.wpforms-form' ), formID = form.data( 'formid' ), fieldID = element.closest( '.wpforms-field' ).data( 'field-id' ); let properties; if ( typeof window[ 'wpforms_' + formID + '_' + fieldID ] !== 'undefined' && window[ 'wpforms_' + formID + '_' + fieldID ].hasOwnProperty( 'datepicker' ) ) { properties = window[ 'wpforms_' + formID + '_' + fieldID ].datepicker; } else if ( typeof window[ 'wpforms_' + formID ] !== 'undefined' && window[ 'wpforms_' + formID ].hasOwnProperty( 'datepicker' ) ) { properties = window[ 'wpforms_' + formID ].datepicker; } else if ( typeof wpforms_datepicker !== 'undefined' ) { properties = wpforms_datepicker; } else { properties = { disableMobile: true, }; } // Redefine locale only if user doesn't do that manually, and we have the locale. if ( ! properties.hasOwnProperty( 'locale' ) && typeof wpforms_settings !== 'undefined' && wpforms_settings.hasOwnProperty( 'locale' ) ) { properties.locale = wpforms_settings.locale; } properties.wrap = true; properties.dateFormat = $input.data( 'date-format' ); if ( $input.data( 'disable-past-dates' ) === 1 ) { properties.minDate = 'today'; if ( $input.data( 'disable-todays-date' ) === 1 ) { const date = new Date(); properties.minDate = date.setDate( date.getDate() + 1 ); } } let limitDays = $input.data( 'limit-days' ); const weekDays = [ 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat' ]; if ( limitDays && limitDays !== '' ) { limitDays = limitDays.split( ',' ); properties.disable = [ function( date ) { let limitDay = null; for ( const i in limitDays ) { limitDay = weekDays.indexOf( limitDays[ i ] ); if ( limitDay === date.getDay() ) { return false; } } return true; } ]; } // Toggle clear date icon. properties.onChange = function( selectedDates, dateStr, instance ) { // eslint-disable-line no-unused-vars element.find( '.wpforms-datepicker-clear' ) .css( 'display', dateStr === '' ? 'none' : 'block' ); }; element.flatpickr( properties ); } ); }, /** * Load jQuery Time Picker. * * @since 1.2.3 * @since 1.8.9 Added the `$context` parameter. * * @param {jQuery} $context Container to search for datepicker elements. */ loadTimePicker( $context ) { // Only load if jQuery timepicker library exists. if ( typeof $.fn.timepicker === 'undefined' ) { return; } $context = $context?.length ? $context : $( document ); $context.find( '.wpforms-timepicker' ).each( function() { // eslint-disable-line complexity const element = $( this ), form = element.closest( '.wpforms-form' ), formID = form.data( 'formid' ), fieldID = element.closest( '.wpforms-field' ).data( 'field-id' ); let properties; if ( typeof window[ 'wpforms_' + formID + '_' + fieldID ] !== 'undefined' && window[ 'wpforms_' + formID + '_' + fieldID ].hasOwnProperty( 'timepicker' ) ) { properties = window[ 'wpforms_' + formID + '_' + fieldID ].timepicker; } else if ( typeof window[ 'wpforms_' + formID ] !== 'undefined' && window[ 'wpforms_' + formID ].hasOwnProperty( 'timepicker' ) ) { properties = window[ 'wpforms_' + formID ].timepicker; } else if ( typeof wpforms_timepicker !== 'undefined' ) { properties = wpforms_timepicker; } else { properties = { scrollDefault: 'now', forceRoundTime: true, }; } // Retrieve the value from the input element. const inputValue = element.val(); element.timepicker( properties ); // Check if a value is available. if ( inputValue ) { // Set the input element's value to the retrieved value. element.val( inputValue ); // Trigger the 'changeTime' event to update the timepicker after programmatically setting the value. element.trigger( 'changeTime' ); } } ); }, /** * Load jQuery input masks. * * @since 1.2.3 * @since 1.8.9 Added the `$context` parameter. * * @param {jQuery} $context Container to search for datepicker elements. */ loadInputMask( $context ) { // Only load if jQuery input mask library exists. if ( typeof $.fn.inputmask === 'undefined' ) { return; } $context = $context?.length ? $context : $( document ); // This setting has no effect when switching to the "RTL" mode. $context.find( '.wpforms-masked-input' ).inputmask( { rightAlign: false } ); }, /** * Fix the Phone field snippets. * * @since 1.8.7.1 * @deprecated 1.9.2 * * @param {jQuery} $field Phone field element. */ fixPhoneFieldSnippets( $field ) { // eslint-disable-next-line no-console console.warn( 'WARNING! Obsolete function called. Function wpforms.fixPhoneFieldSnippets( $field ) has been deprecated, please use the wpforms.repairSmartPhoneHiddenField( $field ) function instead!' ); $field.siblings( 'input[type="hidden"]' ).each( function() { if ( ! $( this ).attr( 'name' ).includes( 'function' ) ) { return; } const data = $field.data( 'plugin_intlTelInput' ); const options = data.d || data.options; if ( ! options ) { return; } const insta = window.intlTelInputGlobals.getInstance( $field[ 0 ] ); insta.destroy(); options.initialCountry = options.initialCountry.toLowerCase(); options.onlyCountries = options.onlyCountries.map( ( v ) => v.toLowerCase() ); options.preferredCountries = options.preferredCountries.map( ( v ) => v.toLowerCase() ); window.intlTelInput( $field[ 0 ], options ); $field.siblings( 'input[type="hidden"]' ).each( function() { const $hiddenInput = $( this ); $hiddenInput.attr( 'name', $hiddenInput.attr( 'name' ).replace( 'wpf-temp-', '' ) ); } ); } ); }, /** * Compatibility fix with an old intl-tel-input library that may include in other addons. * Also, for custom snippets that use `options.hiddenInput` to recieve fieldId. * * @since 1.9.2 * @deprecated 1.9.4 * * @param {jQuery} $field Phone field element. */ repairSmartPhoneHiddenField( $field ) { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "wpforms.repairSmartPhoneHiddenField()" has been deprecated, please use the new "WPFormsPhoneField.repairSmartHiddenField()" function instead!' ); WPFormsPhoneField?.repairSmartHiddenField?.( $field ); }, /** * Get a list of default smart phone field options. * * @since 1.9.2 * @deprecated 1.9.4 * * @return {Object} List of default options. */ getDefaultSmartPhoneFieldOptions() { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "wpforms.getDefaultSmartPhoneFieldOptions()" has been deprecated, please use the new "WPFormsPhoneField.getDefaultSmartFieldOptions()" function instead!' ); return WPFormsPhoneField?.getDefaultSmartFieldOptions?.(); }, /** * Load Smartphone field. * * @since 1.5.2 * @since 1.8.9 Added the `$context` parameter. * @deprecated 1.9.4 * * @param {jQuery} $context Context to search for smartphone elements. */ loadSmartPhoneField( $context ) { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "wpforms.loadSmartPhoneField()" has been deprecated, please use the new "WPFormsPhoneField.loadSmartField()" function instead!' ); WPFormsPhoneField?.loadSmartField?.( $context ); }, /** * Backward compatibility jQuery plugin for IntlTelInput library, to support custom snippets. * e.g., https://wpforms.com/developers/how-to-set-a-default-flag-on-smart-phone-field-with-gdpr/. * * @since 1.9.2 * @deprecated 1.9.4 */ loadJqueryIntlTelInput() { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "wpforms.loadJqueryIntlTelInput()" has been deprecated, please use the new "WPFormsPhoneField.loadJqueryIntlTelInput()" function instead!' ); WPFormsPhoneField?.loadJqueryIntlTelInput?.(); }, /** * Init smart phone field. * * @since 1.9.2 * @deprecated 1.9.4 * * @param {jQuery} $el Input field. * @param {Object} inputOptions Options for intlTelInput. */ initSmartPhoneField( $el, inputOptions ) { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "wpforms.initSmartPhoneField()" has been deprecated, please use the new "WPFormsPhoneField.initSmartField()" function instead!' ); WPFormsPhoneField?.initSmartField?.( $el, inputOptions ); }, /** * Bind Smartphone field event. * * @since 1.8.9 * @deprecated 1.9.4 */ bindSmartPhoneField() { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "wpforms.bindSmartPhoneField()" has been deprecated, please use the new "WPFormsPhoneField.bindSmartField()" function instead!' ); WPFormsPhoneField?.bindSmartField?.(); }, /** * Payments: Do various payment-related tasks on a load. * * @since 1.2.6 */ loadPayments() { // Update Total field(s) with the latest calculation. $( '.wpforms-payment-total' ).each( function( index, el ) { app.amountTotal( this ); } ); // Credit card validation. if ( typeof $.fn.payment !== 'undefined' ) { $( '.wpforms-field-credit-card-cardnumber' ).payment( 'formatCardNumber' ); $( '.wpforms-field-credit-card-cardcvc' ).payment( 'formatCardCVC' ); } }, /** * Load mailcheck. * * @since 1.5.3 */ loadMailcheck() { // eslint-disable-line max-lines-per-function // Skip loading if `wpforms_mailcheck_enabled` filter return false. if ( ! wpforms_settings.mailcheck_enabled ) { return; } // Only load if a library exists. if ( typeof $.fn.mailcheck === 'undefined' ) { return; } if ( wpforms_settings.mailcheck_domains.length > 0 ) { Mailcheck.defaultDomains = Mailcheck.defaultDomains.concat( wpforms_settings.mailcheck_domains ); } if ( wpforms_settings.mailcheck_toplevel_domains.length > 0 ) { Mailcheck.defaultTopLevelDomains = Mailcheck.defaultTopLevelDomains.concat( wpforms_settings.mailcheck_toplevel_domains ); } // Mailcheck suggestion. $( document ).on( 'blur', '.wpforms-field-email input', function() { const $input = $( this ), id = $input.attr( 'id' ); $input.mailcheck( { suggested( $el, suggestion ) { // decodeURI() will throw an error if the percent sign is not followed by two hexadecimal digits. suggestion.full = suggestion.full.replace( /%(?![0-9][0-9a-fA-F]+)/g, '%25' ); suggestion.address = suggestion.address.replace( /%(?![0-9][0-9a-fA-F]+)/g, '%25' ); suggestion.domain = suggestion.domain.replace( /%(?![0-9][0-9a-fA-F]+)/g, '%25' ); if ( suggestion.address.match( /^xn--/ ) ) { suggestion.full = punycode.toUnicode( decodeURI( suggestion.full ) ); const parts = suggestion.full.split( '@' ); suggestion.address = parts[ 0 ]; suggestion.domain = parts[ 1 ]; } if ( suggestion.domain.match( /^xn--/ ) ) { suggestion.domain = punycode.toUnicode( decodeURI( suggestion.domain ) ); } const address = decodeURI( suggestion.address ).replaceAll( /[<>'"()/\\|:;=@%&\s]/ig, '' ).substr( 0, 64 ), domain = decodeURI( suggestion.domain ).replaceAll( /[<>'"()/\\|:;=@%&+_\s]/ig, '' ); suggestion = '' + address + '@' + domain + ''; suggestion = wpforms_settings.val_email_suggestion.replace( '{suggestion}', suggestion ); $el.closest( '.wpforms-field' ).find( '#' + id + '_suggestion' ).remove(); $el.parent().append( '' ); }, empty() { $( '#' + id + '_suggestion' ).remove(); }, } ); } ); // Apply a Mailcheck suggestion. $( document ).on( 'click', '.wpforms-field-email .mailcheck-suggestion', function( e ) { const $suggestion = $( this ), $field = $suggestion.closest( '.wpforms-field' ), id = $suggestion.data( 'id' ); e.preventDefault(); $field.find( '#' + id ).val( $suggestion.text() ); $suggestion.parent().remove(); } ); }, /** * Load Choices.js library for all Modern style Dropdown fields (` like a screen-reader text. // It's important for field validation. $element .removeAttr( 'hidden' ) .addClass( self.config.classNames.input + '--hidden' ); // Add CSS-class for size. if ( sizeClass ) { $( self.containerOuter.element ).addClass( sizeClass ); } /** * If a multiple select has selected choices - hide a placeholder text. * In case if select is empty - we return placeholder text. */ if ( $element.prop( 'multiple' ) ) { // On init event. $input.data( 'placeholder', $input.attr( 'placeholder' ) ).css( 'width', 'auto' ); if ( self.getValue( true ).length ) { $input.removeAttr( 'placeholder' ); } $input.css( 'width', '1ch' ); } // On change event. $element.on( 'change', function() { // Listen if multiple select has choices. if ( $element.prop( 'multiple' ) ) { // eslint-disable-next-line no-unused-expressions self.getValue( true ).length ? $input.removeAttr( 'placeholder' ) : $input.attr( 'placeholder', $input.data( 'placeholder' ) ).css( 'width', 'auto' ); } const validator = $element.closest( 'form' ).data( 'validator' ); if ( ! validator ) { return; } validator.element( $element ); } ); }; args.callbackOnCreateTemplates = function() { const self = this, $element = $( self.passedElement.element ); return { // Change default template for option. option( item ) { const opt = Choices.defaults.templates.option.call( this, item ); // Add a `.placeholder` class for placeholder option - it needs for WPForm CL. if ( 'undefined' !== typeof item.placeholder && true === item.placeholder ) { opt.classList.add( 'placeholder' ); } // Add a `data-amount` attribute for payment dropdown. // It will be a copy from a Choices.js `data-custom-properties` attribute. if ( $element.hasClass( 'wpforms-payment-price' ) && 'undefined' !== typeof item.customProperties && null !== item.customProperties ) { opt.dataset.amount = item.customProperties; } return opt; }, }; }; // Save choicesjs instance for future access. $( el ).data( 'choicesjs', new Choices( el, args ) ); } ); }, /** * Bind ChoicesJS' events. * * @since 1.8.9 */ bindChoicesJS() { // Add the ability to close the drop-down menu on the frontend. $( document ).on( 'click', '.choices', function( e ) { const $choices = $( this ), choicesObj = $choices.find( 'select' ).data( 'choicesjs' ); if ( choicesObj && $choices.hasClass( 'is-open' ) && ( e.target.classList.contains( 'choices__inner' ) || e.target.classList.contains( 'choices__arrow' ) ) ) { choicesObj.hideDropdown(); } } ); }, //--------------------------------------------------------------------// // Binds. //--------------------------------------------------------------------// /** * Element bindings. * * @since 1.2.3 */ bindUIActions() { // eslint-disable-line max-lines-per-function const $document = $( document ); // Pagebreak navigation. $document.on( 'click', '.wpforms-page-button', function( event ) { event.preventDefault(); app.pagebreakNav( this ); } ); // Payments: Update Total field(s) when latest calculation. let debounceTimerPrice; $document.on( 'change input', '.wpforms-payment-price', function() { // Using the setTimeout with an interval of 0, // we can defer the execution of the app.amountTotal() function. clearTimeout( debounceTimerPrice ); debounceTimerPrice = setTimeout( () => { app.amountTotal( this, true ); }, 0 ); } ); // Payments: Update Total field(s) when changing quantity. let debounceTimerQuantity; $document.on( 'change input', 'select.wpforms-payment-quantity', function() { // Using the setTimeout with an interval of 0, // we can defer the execution of the app.amountTotal() function. clearTimeout( debounceTimerQuantity ); debounceTimerQuantity = setTimeout( () => { app.amountTotal( this, true ); app.updateOrderSummaryItemQuantity( $( this ) ); }, 0 ); } ); // Payments: Restrict user input payment fields. $document.on( 'input', '.wpforms-payment-user-input', function() { const $this = $( this ), amount = $this.val(); $this.val( amount.replace( /[^0-9.,]/g, '' ) ); } ); // Payments: Sanitize/format user input amounts. $document.on( 'focusout', '.wpforms-payment-user-input', function() { const $this = $( this ), amount = $this.val(); if ( ! amount ) { return amount; } const sanitized = app.amountSanitize( amount ), formatted = app.amountFormat( sanitized ); $this.val( formatted ); } ); // Payments: Update Total field(s) when conditionals are processed. let debounceTimerConditions; $document.on( 'wpformsProcessConditionals', function( e, el ) { // Using the setTimeout with an interval of 0, // we can defer the execution of the app.amountTotal() function. clearTimeout( debounceTimerConditions ); debounceTimerConditions = setTimeout( () => { app.amountTotal( el, true ); }, 0 ); } ); // Rating field: hover effect. $document.on( 'mouseenter', '.wpforms-field-rating-item', function() { $( this ).parent().find( '.wpforms-field-rating-item' ).removeClass( 'selected hover' ); $( this ).prevAll().addBack().addClass( 'hover' ); } ).on( 'mouseleave', '.wpforms-field-rating-item', function() { $( this ).parent().find( '.wpforms-field-rating-item' ).removeClass( 'selected hover' ); $( this ).parent().find( 'input:checked' ).parent().prevAll().addBack().addClass( 'selected' ); } ); // Rating field: toggle selected state. $( document ).on( 'change', '.wpforms-field-rating-item input', function() { const $this = $( this ), $wrap = $this.closest( '.wpforms-field-rating-items' ), $items = $wrap.find( '.wpforms-field-rating-item' ); $this.focus(); // Enable keyboard navigation. $items.removeClass( 'hover selected' ); $this.parent().prevAll().addBack().addClass( 'selected' ); } ); // Rating field: preselect the selected rating (from dynamic/fallback population). $( function() { $( '.wpforms-field-rating-item input:checked' ).trigger( 'change' ); } ); // Checkbox/Radio/Payment checkbox: make labels keyboard-accessible. $document.on( 'keydown', '.wpforms-image-choices-item label', function( event ) { const $label = $( this ), $field = $label.closest( '.wpforms-field' ); if ( $field.hasClass( 'wpforms-conditional-hide' ) ) { event.preventDefault(); return false; } // Cause the input to be clicked when pressing Space bar on the label. if ( event.keyCode !== 32 ) { return; } $label.find( 'input' ).trigger( 'click' ); event.preventDefault(); } ); // IE: Click on the `image choice` image should trigger the click event on the input (checkbox or radio) field. if ( window.document.documentMode ) { $document.on( 'click', '.wpforms-image-choices-item img', function() { $( this ).closest( 'label' ).find( 'input' ).trigger( 'click' ); } ); } $document.on( 'change', '.wpforms-field-checkbox input, .wpforms-field-radio input, .wpforms-field-payment-multiple input, .wpforms-field-payment-checkbox input, .wpforms-field-gdpr-checkbox input', function( event ) { const $this = $( this ), $field = $this.closest( '.wpforms-field' ); if ( $field.hasClass( 'wpforms-conditional-hide' ) ) { event.preventDefault(); return false; } switch ( $this.attr( 'type' ) ) { case 'radio': $this.closest( 'ul' ).find( 'li' ).removeClass( 'wpforms-selected' ).find( 'input[type=radio]' ).removeProp( 'checked' ); $this .prop( 'checked', true ) .closest( 'li' ).addClass( 'wpforms-selected' ); break; case 'checkbox': if ( $this.is( ':checked' ) ) { $this.closest( 'li' ).addClass( 'wpforms-selected' ); $this.prop( 'checked', true ); } else { $this.closest( 'li' ).removeClass( 'wpforms-selected' ); $this.prop( 'checked', false ); } break; } } ); // Upload fields: Check combined file size. $document.on( 'input', '.wpforms-field-file-upload', function() { const $this = $( this ), $uploads = $this.closest( 'form.wpforms-form' ).find( '.wpforms-field-file-upload input:not(".dropzone-input")' ); let totalSize = 0, postMaxSize = Number( wpforms_settings.post_max_size ), errorMsg = '
    ' + wpforms_settings.val_post_max_size + '
    '; const errorCntTpl = '
    {errorMsg}
    '; const $submitCnt = $this.closest( 'form.wpforms-form' ).find( '.wpforms-submit-container' ); let $submitBtn = $submitCnt.find( 'button.wpforms-submit' ), $errorCnt = $submitCnt.prev(); const $form = $submitBtn.closest( 'form' ), $btnNext = $form.find( '.wpforms-page-next:visible' ); // For multi-pages layout, use the "Next" button instead of the primary "Submit" button. if ( $form.find( '.wpforms-page-indicator' ).length !== 0 && $btnNext.length !== 0 ) { $submitBtn = $btnNext; } // Calculating totalSize. $uploads.each( function() { const $upload = $( this ); let i = 0; const len = $upload[ 0 ].files.length; for ( ; i < len; i++ ) { totalSize += $upload[ 0 ].files[ i ].size; } } ); // Checking totalSize. if ( totalSize < postMaxSize ) { // Remove error and release submit button. $errorCnt.find( '.wpforms-error-container-post_max_size' ).remove(); $submitBtn.prop( 'disabled', false ); WPFormsUtils.triggerEvent( $form, 'wpformsFormSubmitButtonRestore', [ $form, $submitBtn ] ); WPFormsUtils.triggerEvent( $form, 'wpformsCombinedUploadsSizeOk', [ $form, $errorCnt ] ); return; } // Convert sizes to Mb. totalSize = Number( ( totalSize / 1048576 ).toFixed( 3 ) ); postMaxSize = Number( ( postMaxSize / 1048576 ).toFixed( 3 ) ); // Preparing error message. errorMsg = errorMsg.replace( /{totalSize}/, totalSize ).replace( /{maxSize}/, postMaxSize ); // Output error message. if ( $errorCnt.hasClass( 'wpforms-error-container' ) ) { $errorCnt.find( '.wpforms-error-container-post_max_size' ).remove(); $errorCnt.append( errorMsg ); } else { $submitCnt.before( errorCntTpl.replace( /{errorMsg}/, errorMsg ) ); $errorCnt = $submitCnt.prev(); } // Disable submit button. $submitBtn.prop( 'disabled', true ); WPFormsUtils.triggerEvent( $form, 'wpformsFormSubmitButtonDisable', [ $form, $submitBtn ] ); WPFormsUtils.triggerEvent( $form, 'wpformsCombinedUploadsSizeError', [ $form, $errorCnt ] ); } ); // Number Slider field: update hints. $document.on( 'change input', '.wpforms-field-number-slider input[type=range]', function( event ) { const hintEl = $( event.target ).siblings( '.wpforms-field-number-slider-hint' ); hintEl.html( hintEl.data( 'hint' ).replaceAll( '{value}', '' + event.target.value + '' ) ); } ); // Enter key event. $document.on( 'keydown', '.wpforms-form input', function( e ) { if ( e.keyCode !== 13 ) { return; } const $t = $( this ), $page = $t.closest( '.wpforms-page' ); if ( $page.length === 0 ) { return; } if ( [ 'text', 'tel', 'number', 'email', 'url', 'radio', 'checkbox' ].indexOf( $t.attr( 'type' ) ) < 0 ) { return; } if ( $t.hasClass( 'wpforms-datepicker' ) ) { $t.flatpickr( 'close' ); } e.preventDefault(); if ( $page.hasClass( 'last' ) ) { $page.closest( '.wpforms-form' ).find( '.wpforms-submit' ).trigger( 'click' ); return; } $page.find( '.wpforms-page-next' ).trigger( 'click' ); } ); // Allow only numbers, minus and decimal point to be entered into the Numbers field. $document.on( 'keypress', '.wpforms-field-number input', function( e ) { return /^[-0-9.]+$/.test( String.fromCharCode( e.keyCode || e.which ) ); } ); // Start anti-spam timer on interaction of the form fields. $document .one( 'input', '.wpforms-field input, .wpforms-field textarea, .wpforms-field select', app.formChanged ) .one( 'change', '.wpforms-field-select-style-modern, .wpforms-timepicker', app.formChanged ) .one( 'focus', '.dropzone-input', app.formChanged ) .one( 'click touchstart', '.wpforms-signature-canvas', app.formChanged ) .one( 'wpformsRichTextContentChange', app.richTextContentChanged ); $( 'form.wpforms-form' ).on( 'wpformsBeforePageChange', app.skipEmptyPages ); }, /** * Skip empty pages (by CL, hidden fields etc.) inside multi-steps forms. * * @since 1.8.5 * * @param {Event} event Event. * @param {number} nextPage Next page. * @param {jQuery} $form Current form. * @param {string} action The navigation action. */ skipEmptyPages( event, nextPage, $form, action ) { const nextNonEmptyPage = app.findNonEmptyPage( nextPage, $form, action ); if ( nextNonEmptyPage === nextPage ) { return; } event.preventDefault(); if ( nextNonEmptyPage === 1 && action === 'prev' ) { const $secondPage = $form.find( '.wpforms-page-2' ); const $currentPage = $form.find( '.wpforms-page-' + nextPage ); // The previous button is optional. We pass the fallback to the original previous button // in the case when the previous button on the second page does not exist. const $prevButton = $secondPage.find( '.wpforms-page-prev' ).length ? $secondPage.find( '.wpforms-page-prev' ) : $currentPage.find( '.wpforms-page-prev' ); wpforms.navigateToPage( $prevButton, 'prev', 2, $form, $secondPage ); return; } // The next page button is always visible. // So we take the previous page before the next non-empty page // and simulate a jump forward from the next page. const prevPage = nextNonEmptyPage - 1; const $previousPage = $form.find( '.wpforms-page-' + prevPage ); wpforms.navigateToPage( $previousPage.find( '.wpforms-page-next' ), 'next', prevPage, $form, $previousPage ); }, /** * Find the next non-empty page. * * @since 1.8.5 * * @param {number} page Current page. * @param {jQuery} $form Current form. * @param {string} action The navigation action. * * @return {number} The next non-empty page number. */ findNonEmptyPage( page, $form, action ) { let nextNonEmptyPage = page; while ( app.isEmptyPage( $form, nextNonEmptyPage ) ) { if ( action === 'prev' ) { nextNonEmptyPage--; } else { nextNonEmptyPage++; } } return nextNonEmptyPage; }, /** * Check the target page is empty. * * @since 1.8.5 * * @param {jQuery} $form Current form. * @param {number} page Page number. * * @return {boolean} True if page is empty. */ isEmptyPage( $form, page ) { // The first page is always visible. if ( page === 1 ) { return false; } const $currentPage = $form.find( '.wpforms-page-' + page ); // The last page has the "Submit" button, so it's always non-empty. if ( $currentPage.hasClass( 'last' ) ) { return false; } const $fieldsOnPage = $currentPage.find( '.wpforms-field:not(.wpforms-field-pagebreak):not(.wpforms-field-hidden)' ); return $currentPage.find( '.wpforms-conditional-hide' ).length === $fieldsOnPage.length; }, /** * Form changed. * * @since 1.8.3 * * @param {Object} event Event object. */ formChanged( event ) { const $form = $( this ).closest( '.wpforms-form' ); app.maybeSetStartTime( $form ); }, /** * Rich text content changed. * * @since 1.8.3 * * @param {Object} event Event object. * @param {Object} mutation Mutation object. * @param {Object} editor Editor object. */ richTextContentChanged( event, mutation, editor ) { const container = editor.getContainer(); const $form = $( container ).closest( '.wpforms-form' ); app.maybeSetStartTime( $form ); }, /** * Initialize the start timestamp for each form on the page. * * @since 1.9.0 */ initFormsStartTime() { $( '.wpforms-form' ).each( function() { app.maybeSetStartTime( $( this ) ); } ); }, /** * Maybe set start time for anti-spam timer. * * @since 1.8.3 * * @param {jQuery} $form Form element. */ maybeSetStartTime( $form ) { if ( ! $form.data( 'start_timestamp' ) ) { $form.data( 'start_timestamp', app.getTimestampSec() ); } }, /** * Entry preview field callback for a page changing. * * @since 1.6.9 * @deprecated 1.7.0 * * @param {Event} event Event. * @param {number} currentPage Current page. * @param {jQuery} $form Current form. */ entryPreviewFieldPageChange( event, currentPage, $form ) { // eslint-disable-next-line no-console console.warn( 'WARNING! Obsolete function called. Function wpforms.entryPreviewFieldPageChange has been deprecated, please use the WPFormsEntryPreview.pageChange function instead!' ); WPFormsEntryPreview.pageChange( event, currentPage, $form ); }, /** * Update the entry preview fields on the page. * * @since 1.6.9 * @deprecated 1.7.0 * * @param {number} currentPage Current page. * @param {jQuery} $form Current form. */ entryPreviewFieldUpdate( currentPage, $form ) { // eslint-disable-next-line no-console console.warn( 'WARNING! Obsolete function called. Function wpforms.entryPreviewFieldUpdate has been deprecated, please use the WPFormsEntryPreview.update function instead!' ); WPFormsEntryPreview.update( currentPage, $form ); }, /** * Scroll to and focus on the field with error. * * @since 1.5.8 * * @param {jQuery} $el Form, container or input element jQuery object. */ scrollToError( $el ) { if ( $el.length === 0 ) { return; } // Look for a field with an error inside an $el. let $field = $el.find( '.wpforms-field.wpforms-has-error' ); // Look outside in not found inside. if ( $field.length === 0 ) { $field = $el.closest( '.wpforms-field' ); } if ( $field.length === 0 ) { return; } const offset = $field.offset(); if ( typeof offset === 'undefined' ) { return; } app.animateScrollTop( offset.top - 75, 750 ).done( function() { const $error = $field.find( '.wpforms-error' ).first(); if ( typeof $error.focus === 'function' ) { $error.trigger( 'focus' ); } } ); }, /** * Update Pagebreak navigation. * * @since 1.2.2 * * @param {jQuery} el jQuery element object. */ pagebreakNav( el ) { const $this = $( el ), action = $this.data( 'action' ), page = $this.data( 'page' ), $form = $this.closest( '.wpforms-form' ), $page = $form.find( '.wpforms-page-' + page ); app.saveTinyMCE(); if ( 'next' === action && ( typeof $.fn.validate !== 'undefined' ) ) { app.checkForInvalidFields( $form, $page, function() { app.navigateToPage( $this, action, page, $form, $page ); } ); return; } if ( 'prev' === action || 'next' === action ) { app.navigateToPage( $this, action, page, $form, $page ); } }, /** * Check the validity of all the fields in the current page. * * @since 1.7.6 * * @param {jQuery} $form WPForms element object. * @param {jQuery} $page Current page element object in page break context. * @param {Function} callback Callback to run when all fields are valid. */ checkForInvalidFields( $form, $page, callback ) { const validator = $form.data( 'validator' ); if ( ! validator ) { return; } let valid = true; $page.find( ':input' ).each( function( index, el ) { const $el = $( el ); // Skip input fields without `name` attribute, which could have fields. // E.g. `Placeholder` input for Modern dropdown. if ( ! $el.attr( 'name' ) ) { return; } // Skip validation for some fields. // E.g., applied coupon hidden field. if ( $el.hasClass( 'wpforms-field-skip-validation' ) ) { return; } if ( ! $( el ).valid() ) { valid = false; } } ); if ( validator.pendingRequest > 0 ) { setTimeout( function() { app.checkForInvalidFields( $form, $page, callback ); }, 800 ); return; } if ( ! valid ) { app.scrollToError( $page ); } else { callback(); } }, /** * Navigate through page break pages. * * @since 1.7.6 * * @param {jQuery} $this jQuery element of the next / prev nav button. * @param {string} action The navigation action. * @param {number} page Current page number. * @param {jQuery} $form WPForms element object. * @param {jQuery} $page Current page element object in page break context. */ navigateToPage( $this, action, page, $form, $page ) { if ( $this.hasClass( 'wpforms-disabled' ) ) { return; } let nextPage = page; if ( 'next' === action ) { nextPage += 1; } else if ( 'prev' === action ) { nextPage -= 1; } const event = WPFormsUtils.triggerEvent( $this, 'wpformsBeforePageChange', [ nextPage, $form, action ] ); // Allow callbacks on `wpformsBeforePageChange` to cancel page changing by triggering `event.preventDefault()`. if ( event.isDefaultPrevented() ) { return; } $form.find( '.wpforms-page' ).hide(); const $destinationPage = $form.find( '.wpforms-page-' + nextPage ); $destinationPage.show(); app.toggleReCaptchaAndSubmitDisplay( $form, action, $destinationPage ); app.checkTurnstileVisibility( $form ); const pageScroll = app.getPageScroll( $form ); if ( pageScroll ) { app.animateScrollTop( $form.offset().top - pageScroll, 750, null ); } $this.trigger( 'wpformsPageChange', [ nextPage, $form, action ] ); app.manipulateIndicator( nextPage, $form ); }, /** * Toggle the reCaptcha and submit container display. * * @since 1.7.6 * * @param {jQuery} $form WPForms element object. * @param {string} action The navigation action. * @param {jQuery} $destinationPage Destination Page element object. */ toggleReCaptchaAndSubmitDisplay( $form, action, $destinationPage ) { const $submit = $form.find( '.wpforms-submit-container' ), $reCAPTCHA = $form.find( '.wpforms-recaptcha-container' ); if ( 'next' === action && $destinationPage.hasClass( 'last' ) ) { $reCAPTCHA.show(); $submit.show(); } else if ( 'prev' === action ) { $reCAPTCHA.hide(); $submit.hide(); } }, /** * Update Turnstile container class if invisible mode is chosen. * * @since 1.9.0 * * @param {jQuery} $form WPForms element object. */ checkTurnstileVisibility( $form ) { const $turnstile = $form.find( '.wpforms-recaptcha-container' ); // Check if Turnstile captcha is enabled. if ( ! $turnstile.hasClass( 'wpforms-is-turnstile' ) ) { return; } const iframeWrapperHeight = $turnstile.find( '.g-recaptcha' ).height(); parseInt( iframeWrapperHeight, 10 ) === 0 ? $turnstile.addClass( 'wpforms-is-turnstile-invisible' ) : $turnstile.removeClass( 'wpforms-is-turnstile-invisible' ); }, /** * Get the page scroll position. * * @since 1.7.6 * * @param {jQuery} $form WPForms element object. * @return {number|boolean} Returns a number if position to page scroll is found. * Otherwise, return `false` if position isn't found. */ getPageScroll( $form ) { if ( false === window.wpforms_pageScroll ) { return false; } if ( ! app.empty( window.wpform_pageScroll ) ) { return window.wpform_pageScroll; } // Page scroll. return $form.find( '.wpforms-page-indicator' ).data( 'scroll' ) !== 0 ? 75 : false; }, /** * Manipulate the indicator. * * @since 1.7.6 * * @param {number} nextPage The next's / destination's page number. * @param {jQuery} $form WPForms element object. */ manipulateIndicator( nextPage, $form ) { const $indicator = $form.find( '.wpforms-page-indicator' ); if ( ! $indicator ) { return; } const theme = $indicator.data( 'indicator' ); if ( 'connector' === theme || 'circles' === theme ) { app.manipulateConnectorAndCirclesIndicator( $indicator, theme, nextPage ); return; } if ( 'progress' === theme ) { app.manipulateProgressIndicator( $indicator, $form, nextPage ); } }, /** * Manipulate 'circles' or 'connector' theme indicator. * * @since 1.7.6 * * @param {jQuery} $indicator The indicator jQuery element object. * @param {string} theme Indicator theme. * @param {number} nextPage The next's / destination's page number. */ manipulateConnectorAndCirclesIndicator( $indicator, theme, nextPage ) { const color = $indicator.data( 'indicator-color' ); $indicator.find( '.wpforms-page-indicator-page' ).removeClass( 'active' ); $indicator.find( '.wpforms-page-indicator-page-' + nextPage ).addClass( 'active' ); $indicator.find( '.wpforms-page-indicator-page-number' ).removeAttr( 'style' ); $indicator.find( '.active .wpforms-page-indicator-page-number' ).css( 'background-color', color ); if ( 'connector' === theme ) { $indicator.find( '.wpforms-page-indicator-page-triangle' ).removeAttr( 'style' ); $indicator.find( '.active .wpforms-page-indicator-page-triangle' ).css( 'border-top-color', color ); } }, /** * Manipulate 'progress' theme indicator. * * @since 1.7.6 * * @param {jQuery} $indicator The indicator jQuery element object. * @param {jQuery} $form WPForms element object. * @param {number} nextPage The next's / destination's page number. */ manipulateProgressIndicator( $indicator, $form, nextPage ) { const $pageTitle = $indicator.find( '.wpforms-page-indicator-page-title' ), $pageSep = $indicator.find( '.wpforms-page-indicator-page-title-sep' ), totalPages = $form.find( '.wpforms-page' ).length, width = ( nextPage / totalPages ) * 100; $indicator.find( '.wpforms-page-indicator-page-progress' ).css( 'width', width + '%' ); $indicator.find( '.wpforms-page-indicator-steps-current' ).text( nextPage ); if ( $pageTitle.data( 'page-' + nextPage + '-title' ) ) { $pageTitle.css( 'display', 'inline' ).text( $pageTitle.data( 'page-' + nextPage + '-title' ) ); $pageSep.css( 'display', 'inline' ); } else { $pageTitle.css( 'display', 'none' ); $pageSep.css( 'display', 'none' ); } }, /** * OptinMonster compatibility. * * Re-initialize after OptinMonster loads to accommodate changes that * have occurred to the DOM. * * @since 1.5.0 */ bindOptinMonster() { // OM v5. document.addEventListener( 'om.Campaign.load', function( event ) { app.ready(); app.optinMonsterRecaptchaReset( event.detail.Campaign.data.id ); } ); document.addEventListener( 'om.Campaign.afterShow', function( event ) { // Init Repeater fields. if ( 'undefined' !== typeof WPFormsRepeaterField ) { WPFormsRepeaterField.ready(); } } ); // OM Legacy. $( document ).on( 'OptinMonsterOnShow', function( event, data, object ) { app.ready(); app.optinMonsterRecaptchaReset( data.optin ); // Init Repeater fields. if ( 'undefined' !== typeof WPFormsRepeaterField ) { WPFormsRepeaterField.ready(); } } ); }, /** * Reset/recreate hCaptcha/reCAPTCHA v2 inside OptinMonster. * * @since 1.5.0 * @since 1.6.4 Added hCaptcha support. * * @param {string} optinId OptinMonster ID. */ optinMonsterRecaptchaReset( optinId ) { const $form = $( '#om-' + optinId ).find( '.wpforms-form' ), $captchaContainer = $form.find( '.wpforms-recaptcha-container' ), $captcha = $form.find( '.g-recaptcha' ); if ( $form.length && $captcha.length ) { const captchaSiteKey = $captcha.attr( 'data-sitekey' ), captchaID = 'recaptcha-' + Date.now(), apiVar = $captchaContainer.hasClass( 'wpforms-is-hcaptcha' ) ? hcaptcha : grecaptcha; $captcha.remove(); $captchaContainer.prepend( '
    ' ); apiVar.render( captchaID, { sitekey: captchaSiteKey, callback() { wpformsRecaptchaCallback( $( '#' + captchaID ) ); }, } ); } }, //--------------------------------------------------------------------// // Other functions. //--------------------------------------------------------------------// /** * Payments: Run amount calculation and update the Total field value. * * @since 1.2.3 * @since 1.5.1 Added support for payment-checkbox field. * * @param {Object} el jQuery DOM object. * @param {boolean} validate Whether to validate or not. */ amountTotal( el, validate ) { validate = validate || false; const $el = $( el ), $form = $el.closest( '.wpforms-form' ), total = app.amountTotalCalc( $form ); if ( ! app.allowAmountTotalCalc( $form, $el, total ) ) { return; } const totalFormattedSymbol = app.amountFormatSymbol( total ); $form.find( '.wpforms-payment-total' ).each( function() { if ( 'hidden' === $( this ).attr( 'type' ) || 'text' === $( this ).attr( 'type' ) ) { $( this ).val( totalFormattedSymbol ); if ( 'text' === $( this ).attr( 'type' ) && validate && $form.data( 'validator' ) ) { $( this ).valid(); } } else { $( this ).text( totalFormattedSymbol ); } } ); app.updateOrderSummaryItems( $form, $el, totalFormattedSymbol ); }, /** * Check if the total amount calculation is allowed. * Cache total amount to avoid multiple triggers. * * @since 1.9.2 * * @param {jQuery} $form Form object. * @param {jQuery} $el Payment field object. * @param {number} total Total amount. * * @return {boolean} True if the total amount calculation is allowed, false otherwise. */ allowAmountTotalCalc( $form, $el, total ) { const formId = $form.data( 'formid' ); if ( app.getCache( formId, 'amountTotal' ) !== total ) { app.updateCache( formId, 'amountTotal', total ); return true; } const type = $el.prop( 'type' ); // Force re-calculation for choices and dropdown fields. if ( type === 'radio' || type === 'select-one' || type === 'checkbox' ) { return true; } return false; }, /** * Update summary table items visibility and total amount. * * @since 1.8.7 * * @param {jQuery} $form Form object. * @param {jQuery} $paymentField Payment field object. * @param {string} total Formatted form total. */ updateOrderSummaryItems( $form, $paymentField, total ) { $form.find( '.wpforms-order-summary-preview' ).each( function() { const $summary = $( this ); if ( total !== '' ) { $summary.find( '.wpforms-order-summary-preview-total .wpforms-order-summary-item-price' ).text( total ); } $form.find( '.wpforms-payment-price' ).each( function() { app.updateOrderSummaryItem( $( this ), $summary ); } ); } ); }, /** * Update value in cache. * * @since 1.9.2 * * @param {string} formId Form ID. * @param {string} key Cache key. * @param {any} value Cache value. */ updateCache( formId, key, value ) { app.cache[ formId ] = app.cache[ formId ] || {}; app.cache[ formId ][ key ] = value; }, /** * Get a value from the cache. * * @since 1.9.2 * * @param {string} formId Form ID. * @param {string} key Cache key. * * @return {any|boolean} Cache value or false if not found. */ getCache( formId, key ) { if ( ! Object.prototype.hasOwnProperty.call( app.cache, formId ) || ! Object.prototype.hasOwnProperty.call( app.cache[ formId ], key ) ) { return false; } return app.cache[ formId ][ key ]; }, /** * Update summary table item visibility and amount. * * @since 1.8.7 * * @param {jQuery} $paymentField Payment field object. * @param {jQuery} $summary Summary object. */ // eslint-disable-next-line complexity updateOrderSummaryItem( $paymentField, $summary ) { if ( ! $paymentField.hasClass( 'wpforms-payment-price' ) ) { return; } const $field = $paymentField.closest( '.wpforms-field' ), fieldId = $field.data( 'field-id' ), type = $paymentField.prop( 'type' ), isFieldVisible = $field.css( 'display' ) === 'block'; if ( type === 'checkbox' || type === 'radio' || type === 'select-one' ) { // Show only selected items. $summary.find( `tr[data-field="${ fieldId }"]` ).each( function() { const choiceID = $( this ).data( 'choice' ); const isChoiceChecked = type === 'select-one' ? choiceID === parseInt( $field.find( 'select' ).val(), 10 ) : $field.find( `input[value="${ choiceID }"]` ).is( ':checked' ); $( this ).toggle( isFieldVisible && isChoiceChecked ); } ); } else { const $item = $summary.find( `tr[data-field="${ fieldId }"]` ), amount = $paymentField.val(); $item.find( '.wpforms-order-summary-item-price' ).text( app.amountFormatSymbol( app.amountSanitize( amount ) ) ); $item.toggle( isFieldVisible ); } if ( ! $field.hasClass( 'wpforms-payment-quantities-enabled' ) ) { app.updateSummaryPriceWidth( $summary ); app.toggleSummaryPlaceholder( $summary ); return; } app.updateOrderSummaryItemQuantity( $paymentField ); }, /** * Update summary table item quantity and price. * * @since 1.8.7 * * @param {jQuery} $input Payment input object. */ updateOrderSummaryItemQuantity( $input ) { const $field = $input.closest( '.wpforms-field' ), $paymentField = $field.find( 'input.wpforms-payment-price, select.wpforms-payment-price' ), $form = $input.closest( '.wpforms-form' ), fieldId = $field.data( 'field-id' ), quantity = app.getPaymentFieldQuantity( $paymentField ), amount = app.getPaymentFieldAmount( $paymentField ), type = $paymentField.prop( 'type' ); $form.find( '.wpforms-order-summary-preview' ).each( function() { const $summary = $( this ); let $item; if ( type === 'checkbox' || type === 'radio' || type === 'select-one' ) { const choiceId = $paymentField.val(); $item = $summary.find( `tr[data-field="${ fieldId }"][data-choice="${ choiceId }"]` ); } else { $item = $summary.find( `tr[data-field="${ fieldId }"]` ); } $item.toggle( quantity > 0 ); // Update field quantity and amount. $item.find( '.wpforms-order-summary-item-quantity' ).text( quantity ); $item.find( '.wpforms-order-summary-item-price' ).text( app.amountFormatSymbol( amount * quantity ) ); app.updateSummaryPriceWidth( $summary ); app.toggleSummaryPlaceholder( $summary ); } ); }, /** * Update summary price column width. * * @since 1.8.7 * * @param {jQuery} $summary Summary table object. */ updateSummaryPriceWidth( $summary ) { const priceColumnWidth = Math.max( $summary.find( '.wpforms-order-summary-preview-coupon-total .wpforms-order-summary-item-price' ).text().length, $summary.find( '.wpforms-order-summary-preview-total .wpforms-order-summary-item-price' ).text().length + 3 ); $summary.find( '.wpforms-order-summary-item-price' ).css( 'width', `${ priceColumnWidth }ch` ); }, /** * Update summary placeholder visibility. * * @since 1.8.7 * * @param {jQuery} $summary Summary table object. */ toggleSummaryPlaceholder( $summary ) { const $placeholder = $summary.find( '.wpforms-order-summary-placeholder' ); let showPlaceholder = true; $summary.find( '.wpforms-order-summary-field' ).each( function() { if ( $( this ).css( 'display' ) !== 'none' ) { showPlaceholder = false; return false; } } ); $placeholder.toggle( showPlaceholder ); }, /** * Payments: Calculate a total amount without formatting. * * @since 1.6.7.1 * * @param {jQuery} $form Form element. * * @return {number} Total amount. */ amountTotalCalc( $form ) { let total = 0; $( '.wpforms-payment-price', $form ).each( function() { const $this = $( this ); if ( $this.closest( '.wpforms-field-payment-single' ).hasClass( 'wpforms-conditional-hide' ) ) { return; } const amount = app.getPaymentFieldAmount( $this ); if ( amount ) { total = Number( total ) + ( amount * app.getPaymentFieldQuantity( $this ) ); } } ); const $document = $( document ); /** * Trigger whe the total amount has been calculated. * * Allow addons to modify the total amount. * * @since 1.8.2.2 * * @param {Object} data Form element and total. */ const event = WPFormsUtils.triggerEvent( $document, 'wpformsAmountTotalCalculate', [ $form, total ] ); total = event.result !== undefined && event.result >= 0 ? event.result : total; /** * Trigger on the end of the process of calculating the total amount. * * @since 1.8.0.2 * * @param {Object} data Form element and total. */ WPFormsUtils.triggerEvent( $document, 'wpformsAmountTotalCalculated', [ $form, total ] ); return total; }, /** * Get payment field sanitized amount. * * @since 1.8.7 * * @param {jQuery} $field Field element. * * @return {number} Sanitized amount. */ // eslint-disable-next-line complexity getPaymentFieldAmount( $field ) { const type = $field.attr( 'type' ); if ( type === 'text' || type === 'hidden' ) { return Number( app.amountSanitize( $field.val() ) ); } if ( ( type === 'radio' || type === 'checkbox' ) && $field.is( ':checked' ) ) { return Number( app.amountSanitize( $field.data( 'amount' ) ) ); } if ( $field.is( 'select' ) && $field.find( 'option:selected' ).length > 0 && $field.find( 'option:selected' ).data( 'amount' ) ) { return Number( app.amountSanitize( $field.find( 'option:selected' ).data( 'amount' ) ) ); } return 0; }, /** * Get payment field quantity. * * @since 1.8.7 * * @param {jQuery} $field Field element. * * @return {number} Quantity value. */ getPaymentFieldQuantity( $field ) { const fieldId = $field.attr( 'id' ), $quantityInput = $( `#${ fieldId }-quantity` ); if ( $quantityInput.length ) { return Number( $quantityInput.val() ); } return 1; }, /** * Sanitize amount and convert to standard format for calculations. * * @since 1.2.6 * * @param {string} amount Amount to sanitize. * * @return {string} Sanitized amount. */ // eslint-disable-next-line complexity amountSanitize( amount ) { const currency = app.getCurrency(); // Convert to string, remove a currency symbol, and allow only numbers, dots, and commas. amount = amount.toString().replace( currency.symbol, '' ).replace( /[^0-9.,]/g, '' ); if ( currency.decimal_sep === ',' ) { if ( currency.thousands_sep === '.' && amount.indexOf( currency.thousands_sep ) !== -1 ) { amount = amount.replace( new RegExp( '\\' + currency.thousands_sep, 'g' ), '' ); } else if ( currency.thousands_sep === '' && amount.indexOf( '.' ) !== -1 ) { amount = amount.replace( /\./g, '' ); } amount = amount.replace( currency.decimal_sep, '.' ); } else if ( currency.thousands_sep === ',' && ( amount.indexOf( currency.thousands_sep ) !== -1 ) ) { amount = amount.replace( new RegExp( '\\' + currency.thousands_sep, 'g' ), '' ); } return app.numberFormat( amount, currency.decimals, '.', '' ); }, /** * Format amount. * * @since 1.2.6 * * @param {string|number} amount Amount to format. * * @return {string} Formatted amount. */ amountFormat( amount ) { const currency = app.getCurrency(); amount = String( amount ); // Format the amount if ( ',' === currency.decimal_sep && ( amount.indexOf( currency.decimal_sep ) !== -1 ) ) { const sepFound = amount.indexOf( currency.decimal_sep ), whole = amount.substr( 0, sepFound ), part = amount.substr( sepFound + 1, amount.length - 1 ); amount = whole + '.' + part; } // Strip "," from the amount (if set as thousands separator) if ( ',' === currency.thousands_sep && ( amount.indexOf( currency.thousands_sep ) !== -1 ) ) { amount = amount.replace( /,/g, '' ); } if ( app.empty( amount ) ) { amount = 0; } return app.numberFormat( amount, currency.decimals, currency.decimal_sep, currency.thousands_sep ); }, /** * Format amount with the currency symbol. * * @since 1.8.4 * * @param {string|number} amount Amount to format. * * @return {string} Formatted amount. */ amountFormatSymbol( amount ) { const currency = app.getCurrency(), amountFormatted = app.amountFormat( amount ); if ( currency.symbol_pos === 'left' ) { return currency.symbol + amountFormatted; } return amountFormatted + ' ' + currency.symbol; }, /** * Get site currency settings. * * @since 1.2.6 * * @return {Object} Currency data object. */ getCurrency() { // eslint-disable-line complexity const currency = { code: 'USD', thousands_sep: ',', // eslint-disable-line camelcase decimals: 2, decimal_sep: '.', // eslint-disable-line camelcase symbol: '$', symbol_pos: 'left', // eslint-disable-line camelcase }; // Backwards compatibility. if ( typeof wpforms_settings.currency_code !== 'undefined' ) { currency.code = wpforms_settings.currency_code; } if ( typeof wpforms_settings.currency_thousands !== 'undefined' ) { currency.thousands_sep = wpforms_settings.currency_thousands; // eslint-disable-line camelcase } if ( typeof wpforms_settings.currency_decimals !== 'undefined' ) { currency.decimals = wpforms_settings.currency_decimals; } if ( typeof wpforms_settings.currency_decimal !== 'undefined' ) { currency.decimal_sep = wpforms_settings.currency_decimal; // eslint-disable-line camelcase } if ( typeof wpforms_settings.currency_symbol !== 'undefined' ) { currency.symbol = wpforms_settings.currency_symbol; } if ( typeof wpforms_settings.currency_symbol_pos !== 'undefined' ) { currency.symbol_pos = wpforms_settings.currency_symbol_pos; // eslint-disable-line camelcase } return currency; }, /** * Format number. * * @see http://locutus.io/php/number_format/ * * @since 1.2.6 * * @param {string} number Number to format. * @param {number} decimals How many decimals should be there. * @param {string} decimalSep What is the decimal separator. * @param {string} thousandsSep What is the thousand separator. * * @return {string} Formatted number. */ numberFormat( number, decimals, decimalSep, thousandsSep ) { // eslint-disable-line complexity number = ( number + '' ).replace( /[^0-9+\-Ee.]/g, '' ); const n = ! isFinite( +number ) ? 0 : +number; const precision = ! isFinite( +decimals ) ? 0 : Math.abs( decimals ); const sep = ( 'undefined' === typeof thousandsSep ) ? ',' : thousandsSep; const dec = ( 'undefined' === typeof decimalSep ) ? '.' : decimalSep; const toFixedFix = function( n, prec ) { const k = Math.pow( 10, prec ); return '' + ( Math.round( n * k ) / k ).toFixed( prec ); }; // @todo: for IE parseFloat(0.55).toFixed(0) = 0; const s = ( precision ? toFixedFix( n, precision ) : '' + Math.round( n ) ).split( '.' ); if ( s[ 0 ].length > 3 ) { s[ 0 ] = s[ 0 ].replace( /\B(?=(?:\d{3})+(?!\d))/g, sep ); } if ( ( s[ 1 ] || '' ).length < precision ) { s[ 1 ] = s[ 1 ] || ''; s[ 1 ] += new Array( precision - s[ 1 ].length + 1 ).join( '0' ); } return s.join( dec ); }, /** * Empty check similar to PHP. * * @see http://locutus.io/php/empty/ * * @since 1.2.6 * * @param {any} mixedVar Variable to check. * * @return {boolean} Whether the var is empty or not. */ empty( mixedVar ) { let undef; let key; let i; let len; const emptyValues = [ undef, null, false, 0, '', '0' ]; for ( i = 0, len = emptyValues.length; i < len; i++ ) { if ( mixedVar === emptyValues[ i ] ) { return true; } } if ( 'object' === typeof mixedVar ) { for ( key in mixedVar ) { if ( mixedVar.hasOwnProperty( key ) ) { return false; } } return true; } return false; }, /** * Set cookie container user UUID. * * @since 1.3.3 */ setUserIdentifier() { // eslint-disable-line complexity if ( ( ( ! window.hasRequiredConsent && typeof wpforms_settings !== 'undefined' && wpforms_settings.uuid_cookie ) || ( window.hasRequiredConsent && window.hasRequiredConsent() ) ) && ! app.getCookie( '_wpfuuid' ) ) { // Generate UUID - http://stackoverflow.com/a/873856/1489528 const s = new Array( 36 ), hexDigits = '0123456789abcdef'; for ( let i = 0; i < 36; i++ ) { s[ i ] = hexDigits.substr( Math.floor( Math.random() * 0x10 ), 1 ); } s[ 14 ] = '4'; // eslint-disable-next-line no-bitwise s[ 19 ] = hexDigits.substr( ( s[ 19 ] & 0x3 ) | 0x8, 1 ); s[ 8 ] = s[ 13 ] = s[ 18 ] = s[ 23 ] = '-'; const uuid = s.join( '' ); app.createCookie( '_wpfuuid', uuid, 3999 ); } }, /** * Create cookie. * * @since 1.3.3 * * @param {string} name Cookie name. * @param {string} value Cookie value. * @param {number} days Whether it should expire and when. */ createCookie( name, value, days ) { let expires = ''; let secure = ''; if ( wpforms_settings.is_ssl ) { secure = ';secure'; } // If we have a "days" value, set it in the expiry of the cookie. if ( days ) { // If -1 is our value, set a session-based cookie instead of a persistent cookie. if ( -1 === days ) { expires = ''; } else { const date = new Date(); date.setTime( date.getTime() + ( days * 24 * 60 * 60 * 1000 ) ); expires = ';expires=' + date.toGMTString(); } } else { expires = ';expires=Thu, 01 Jan 1970 00:00:01 GMT'; } // Write the cookie. document.cookie = name + '=' + value + expires + ';path=/;samesite=strict' + secure; }, /** * Retrieve cookie. * * @since 1.3.3 * * @param {string} name Cookie name. * * @return {string|null} Cookie value or null when it doesn't exist. */ getCookie( name ) { const nameEQ = name + '=', ca = document.cookie.split( ';' ); for ( let i = 0; i < ca.length; i++ ) { let c = ca[ i ]; while ( ' ' === c.charAt( 0 ) ) { c = c.substring( 1, c.length ); } if ( 0 === c.indexOf( nameEQ ) ) { return c.substring( nameEQ.length, c.length ); } } return null; }, /** * Delete cookie. * * @since 1.3.3 * * @param {string} name Cookie name. */ removeCookie( name ) { app.createCookie( name, '', -1 ); }, /** * Get user browser preferred language. * * @since 1.5.2 * @deprecated 1.9.4 * * @return {string} Language code. */ getFirstBrowserLanguage() { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "wpforms.getFirstBrowserLanguage()" has been deprecated, please use the new "WPFormsPhoneField.getFirstBrowserLanguage()" function instead!' ); return WPFormsPhoneField?.getFirstBrowserLanguage?.(); }, /** * Function maps lang code like `el` to `el-GR`. * * @since 1.9.0 * @deprecated 1.9.4 * * @param {string} lang Language code. * * @return {string} Language code with ISO. */ mapLanguageToIso( lang ) { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "wpforms.mapLanguageToIso()" has been deprecated, please use the new "WPFormsPhoneField.mapLanguageToIso()" function instead!' ); return WPFormsPhoneField?.mapLanguageToIso?.( lang ); }, /** * Asynchronously fetches country code using current IP * and executes a callback provided with a country code parameter. * * @since 1.5.2 * @deprecated 1.9.4 * * @param {Function} callback Executes once the fetch is completed. */ currentIpToCountry( callback ) { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "wpforms.currentIpToCountry()" has been deprecated, please use the new "WPFormsPhoneField.currentIpToCountry()" function instead!' ); WPFormsPhoneField?.currentIpToCountry?.( callback ); }, /** * Form submit. * * @since 1.5.3 * @since 1.7.6 Allow canceling form submission. * * @param {jQuery} $form Form element. */ formSubmit( $form ) { // Form element was passed from vanilla JavaScript. if ( ! ( $form instanceof jQuery ) ) { $form = $( $form ); } app.saveTinyMCE(); const event = WPFormsUtils.triggerEvent( $form, 'wpformsBeforeFormSubmit', [ $form ] ); // Allow callbacks on `wpformsBeforeFormSubmit` to cancel form submission by triggering `event.preventDefault()`. if ( event.isDefaultPrevented() ) { app.restoreSubmitButton( $form, $form.closest( '.wpforms-container' ) ); return; } if ( $form.hasClass( 'wpforms-ajax-form' ) && typeof FormData !== 'undefined' ) { app.formSubmitAjax( $form ); } else { app.formSubmitNormal( $form ); } }, /** * Restore default state for the form submit button. * * @since 1.7.6 * * @param {jQuery} $form Form element. * @param {jQuery} $container Form container. */ restoreSubmitButton( $form, $container ) { const $submit = $form.find( '.wpforms-submit' ), submitText = $submit.data( 'submit-text' ); if ( submitText ) { $submit.text( submitText ); } $submit.prop( 'disabled', false ); WPFormsUtils.triggerEvent( $form, 'wpformsFormSubmitButtonRestore', [ $form, $submit ] ); $container.css( 'opacity', '' ); $form.find( '.wpforms-submit-spinner' ).hide(); }, /** * Normal submit of a form with page reload. * * @since 1.5.3 * * @param {jQuery} $form Form element. */ formSubmitNormal( $form ) { if ( ! $form.length ) { return; } const $submit = $form.find( '.wpforms-submit' ), recaptchaID = $submit.get( 0 ).recaptchaID; if ( ! app.empty( recaptchaID ) || recaptchaID === 0 ) { $submit.get( 0 ).recaptchaID = false; } $form.append( '' ); $form.append( '' ); $form.get( 0 ).submit(); }, /** * Does the form have a captcha? * * @since 1.7.6 * * @param {jQuery} $form Form element. * * @return {boolean} True when the form has a captcha. */ formHasCaptcha( $form ) { if ( ! $form || ! $form.length ) { return false; } if ( typeof hcaptcha === 'undefined' && typeof grecaptcha === 'undefined' && typeof turnstile === 'undefined' ) { return false; } const $captchaContainer = $form.find( '.wpforms-recaptcha-container' ); return Boolean( $captchaContainer.length ); }, /** * Reset form captcha. * * @since 1.5.3 * @since 1.6.4 Added hCaptcha support. * * @param {jQuery} $form Form element. */ resetFormRecaptcha( $form ) { // eslint-disable-line complexity if ( ! app.formHasCaptcha( $form ) ) { return; } const $captchaContainer = $form.find( '.wpforms-recaptcha-container' ); let apiVar, recaptchaID; if ( $captchaContainer.hasClass( 'wpforms-is-hcaptcha' ) ) { apiVar = hcaptcha; } else if ( $captchaContainer.hasClass( 'wpforms-is-turnstile' ) ) { apiVar = turnstile; } else { apiVar = grecaptcha; } // Check for invisible recaptcha first. recaptchaID = $form.find( '.wpforms-submit' ).get( 0 ).recaptchaID; // Check for hcaptcha/recaptcha v2, if invisible recaptcha is not found. if ( app.empty( recaptchaID ) && recaptchaID !== 0 ) { recaptchaID = $form.find( '.g-recaptcha' ).data( 'recaptcha-id' ); } // Reset captcha. if ( ! app.empty( recaptchaID ) || recaptchaID === 0 ) { apiVar.reset( recaptchaID ); } }, /** * Console log AJAX error. * * @since 1.5.3 * * @param {string} error Error text (optional). */ consoleLogAjaxError( error ) { if ( error ) { console.error( 'WPForms AJAX submit error:\n%s', error ); // eslint-disable-line no-console } else { console.error( 'WPForms AJAX submit error' ); // eslint-disable-line no-console } }, /** * Display form AJAX errors. * * @since 1.5.3 * * @param {jQuery} $form Form element. * @param {Object} errors Errors in format { general: { generalErrors }, field: { fieldErrors } }. */ displayFormAjaxErrors( $form, errors ) { // eslint-disable-line complexity if ( 'string' === typeof errors ) { app.displayFormAjaxGeneralErrors( $form, errors ); return; } errors = errors && ( 'errors' in errors ) ? errors.errors : null; if ( app.empty( errors ) || ( app.empty( errors.general ) && app.empty( errors.field ) ) ) { app.consoleLogAjaxError(); return; } if ( ! app.empty( errors.general ) ) { app.displayFormAjaxGeneralErrors( $form, errors.general ); } if ( ! app.empty( errors.field ) ) { app.displayFormAjaxFieldErrors( $form, errors.field ); } }, /** * Display form AJAX general errors that cannot be displayed using jQuery Validation plugin. * * @since 1.5.3 * * @param {jQuery} $form Form element. * @param {Object} errors Errors in format { errorType: errorText }. */ displayFormAjaxGeneralErrors( $form, errors ) { // eslint-disable-line complexity if ( ! $form || ! $form.length ) { return; } if ( app.empty( errors ) ) { return; } if ( app.isModernMarkupEnabled() ) { $form.attr( { 'aria-invalid': 'true', 'aria-errormessage': '', } ); } // Safety net for random errors thrown by a third-party code. Should never be used intentionally. if ( 'string' === typeof errors ) { const roleAttr = app.isModernMarkupEnabled() ? ' role="alert"' : '', errPrefix = app.isModernMarkupEnabled() ? `${ wpforms_settings.formErrorMessagePrefix }` : ''; $form .find( '.wpforms-submit-container' ) .before( `
    ${ errPrefix }${ errors }
    ` ); app.setCurrentPage( $form, {} ); return; } const formId = $form.data( 'formid' ); app.printGeneralErrors( $form, errors, formId ); }, /** * Print general errors. * * @since 1.8.3 * * @param {jQuery} $form Form element. * @param {Object} errors Error Object. * @param {string} formId Form ID. */ printGeneralErrors( $form, errors, formId ) { /** * Handle header error. * * @since 1.8.6 * * @param {string} html Error HTML. */ function handleHeaderError( html ) { $form.prepend( html ); } /** * Handle footer error. * * @since 1.8.6 * * @param {string} html Error HTML. */ function handleFooterError( html ) { if ( $form.find( '.wpforms-page-indicator' ).length === 0 ) { $form.find( '.wpforms-submit-container' ).before( html ); } else { // Check if it is a multipage form. // If it is a multipage form, we need error only on the first page. $form.find( '.wpforms-page-1' ).append( html ); } } /** * Handle reCAPTCHA error. * * @since 1.8.6 * * @param {string} html Error HTML. */ function handleRecaptchaError( html ) { $form.find( '.wpforms-recaptcha-container' ).append( html ); } $.each( errors, function( type, html ) { switch ( type ) { case 'header': case 'header_styled': handleHeaderError( html ); break; case 'footer': case 'footer_styled': handleFooterError( html ); break; case 'recaptcha': handleRecaptchaError( html ); break; } if ( app.isModernMarkupEnabled() ) { const errormessage = $form.attr( 'aria-errormessage' ) || ''; $form.attr( 'aria-errormessage', `${ errormessage } wpforms-${ formId }-${ type }-error` ); } } ); if ( $form.find( '.wpforms-error-container' ).length ) { app.animateScrollTop( $form.find( '.wpforms-error-container' ).first().offset().top - 100 ); } }, /** * Clear forms AJAX general errors that cannot be cleared using jQuery Validation plugin. * * @since 1.5.3 * * @param {jQuery} $form Form element. */ clearFormAjaxGeneralErrors( $form ) { $form.find( '.wpforms-error-container' ).remove(); $form.find( '#wpforms-field_recaptcha-error' ).remove(); // Clear form accessibility attributes. if ( app.isModernMarkupEnabled() ) { $form.attr( { 'aria-invalid': 'false', 'aria-errormessage': '', } ); } }, /** * Display form AJAX field errors using jQuery Validation plugin. * * @since 1.5.3 * * @param {jQuery} $form Form element. * @param {Object} errors Errors in format { fieldName: errorText }. */ displayFormAjaxFieldErrors( $form, errors ) { if ( ! $form || ! $form.length ) { return; } if ( app.empty( errors ) ) { return; } const validator = $form.data( 'validator' ); if ( ! validator ) { return; } errors = app.splitFieldErrors( errors ); // Set data attribute for each field with server error. $.each( errors, function( field, message ) { const $field = $( '[name="' + field + '"]', $form ); if ( $field.length ) { $field.attr( 'data-server-error', message ); } else { // unset error, validator.showErrors() will not work if the field is not found. delete errors[ field ]; } } ); validator.showErrors( errors ); if ( ! app.formHasCaptcha( $form ) ) { validator.focusInvalid(); } }, /** * Split field errors. * * @since 1.8.9 * * @param {Object} errors Errors. * * @return {Object} Errors. */ splitFieldErrors: ( errors ) => { $.each( errors, function( field, message ) { if ( 'string' === typeof message ) { return; } // If errors an object consisting of { subfield: errorMessage }, then iterate each to display error. $.each( message, function( subfield, errorMessage ) { // Get the last part of the field (in []) and check if it is the same as subfield. const lastPart = field.split( '[' ).pop().replace( ']', '' ); // Get from the `field` name all except what we caught in `lastPart`. const fieldNameBase = field.replace( '[' + lastPart + ']', '' ); if ( lastPart === subfield ) { errors[ field ] = errorMessage; } else if ( 'string' === typeof subfield && isNaN( subfield ) ) { errors[ fieldNameBase + '[' + subfield + ']' ] = errorMessage; } } ); } ); return errors; }, /** * Submit a form using AJAX. * * @since 1.5.3 * @since 1.7.6 Allow canceling Ajax submission. * * @param {jQuery} $form Form element. * * @return {JQueryXHR|JQueryDeferred} Promise like an object for async callbacks. */ formSubmitAjax: ( $form ) => { // eslint-disable-line max-lines-per-function if ( ! $form.length ) { return $.Deferred().reject(); // eslint-disable-line new-cap } const $container = $form.closest( '.wpforms-container' ), $spinner = $form.find( '.wpforms-submit-spinner' ); let $confirmationScroll; $container.css( 'opacity', 0.6 ); $spinner.show(); app.clearFormAjaxGeneralErrors( $form ); const formData = new FormData( $form.get( 0 ) ); formData.append( 'action', 'wpforms_submit' ); formData.append( 'start_timestamp', $form.data( 'start_timestamp' ) ); formData.append( 'end_timestamp', app.getTimestampSec() ); const args = { type : 'post', dataType : 'json', url : wpforms_settings.ajaxurl, data : formData, cache : false, contentType: false, processData: false, }; args.success = function( json ) { // eslint-disable-line complexity if ( ! json ) { app.consoleLogAjaxError(); return; } if ( json.data && json.data.action_required ) { $form.trigger( 'wpformsAjaxSubmitActionRequired', json ); return; } if ( ! json.success ) { app.resetFormRecaptcha( $form ); app.displayFormAjaxErrors( $form, json.data ); $form.trigger( 'wpformsAjaxSubmitFailed', json ); app.setCurrentPage( $form, json.data ); return; } $form.trigger( 'wpformsAjaxSubmitSuccess', json ); if ( ! json.data ) { return; } if ( json.data.redirect_url ) { const newTab = json.data.new_tab || false; $form.trigger( 'wpformsAjaxSubmitBeforeRedirect', json ); if ( newTab ) { window.open( json.data.redirect_url, '_blank' ); location.reload(); return; } window.location = json.data.redirect_url; return; } if ( json.data.confirmation ) { $container.html( json.data.confirmation ); $confirmationScroll = $container.find( 'div.wpforms-confirmation-scroll' ); $container.trigger( 'wpformsAjaxSubmitSuccessConfirmation', json ); if ( $confirmationScroll.length ) { app.animateScrollTop( $confirmationScroll.offset().top - 100 ); } } }; args.error = function( jqHXR, textStatus, error ) { app.consoleLogAjaxError( error ); $form.trigger( 'wpformsAjaxSubmitError', [ jqHXR, textStatus, error ] ); }; args.complete = function( jqHXR, textStatus ) { /* * Do not make form active if the action is required, or * if the ajax request was successful and the form has a redirect. */ if ( jqHXR.responseJSON && jqHXR.responseJSON.data && ( jqHXR.responseJSON.data.action_required || ( textStatus === 'success' && jqHXR.responseJSON.data.redirect_url ) ) ) { return; } app.restoreSubmitButton( $form, $container ); $form.trigger( 'wpformsAjaxSubmitCompleted', [ jqHXR, textStatus ] ); }; const event = WPFormsUtils.triggerEvent( $form, 'wpformsAjaxBeforeSubmit', [ $form ] ); // Allow callbacks on `wpformsAjaxBeforeSubmit` to cancel Ajax form submission by triggering `event.preventDefault()`. if ( event.isDefaultPrevented() ) { app.restoreSubmitButton( $form, $container ); return $.Deferred().reject(); // eslint-disable-line new-cap } return $.ajax( args ); }, /** * Display page with error for multiple page form. * * @since 1.7.9 * * @param {jQuery} $form Form element. * @param {Object} $json Error json. */ setCurrentPage( $form, $json ) { // eslint-disable-line complexity // Return for one-page forms. if ( $form.find( '.wpforms-page-indicator' ).length === 0 ) { return; } const $errorPages = []; $form.find( '.wpforms-page' ).each( function( index, el ) { if ( $( el ).find( '.wpforms-has-error' ).length >= 1 ) { return $errorPages.push( $( el ) ); } } ); // Do not change the page if there is a captcha error and there are no other field or footer errors. if ( $errorPages.length === 0 && $json.errors !== undefined && $json.errors.general !== undefined && $json.errors.general.footer === undefined && $json.errors.general.recaptcha !== undefined ) { return; } // Get the first page with error. const $currentPage = $errorPages.length > 0 ? $errorPages[ 0 ] : $form.find( '.wpforms-page-1' ); const currentPage = $currentPage.data( 'page' ); let $page, action = 'prev'; // If error is on the first page, or we have general errors among others, go to the first page. if ( currentPage === 1 || ( $json.errors !== undefined && $json.errors.general.footer !== undefined ) ) { $page = $form.find( '.wpforms-page-1' ).next(); } else { $page = $currentPage.next().length !== 0 ? $currentPage.next() : $currentPage.prev(); action = $currentPage.next().length !== 0 ? 'prev' : 'next'; } // Take the page from which navigate to error. const $nextBtn = $page.find( '.wpforms-page-next' ), page = $page.data( 'page' ); // Imitate navigation to the page with error. app.navigateToPage( $nextBtn, action, page, $form, $( '.wpforms-page-' + page ) ); }, /** * Scroll to position with animation. * * @since 1.5.3 * * @param {number} position Position (in pixels) to scroll to, * @param {number} duration Animation duration. * @param {Function} complete Function to execute after animation is complete. * * @return {Promise} A promise object for async callbacks. */ animateScrollTop( position, duration, complete ) { duration = duration || 1000; complete = typeof complete === 'function' ? complete : function() {}; return $( 'html, body' ).animate( { scrollTop: parseInt( position, 10 ) }, { duration, complete } ).promise(); }, /** * Save tinyMCE. * * @since 1.7.0 */ saveTinyMCE() { if ( typeof tinyMCE !== 'undefined' ) { tinyMCE.triggerSave(); } }, /** * Check if an object is a function. * * @deprecated 1.6.7 * * @since 1.5.8 * * @param {any} object Object to check if it is a function. * * @return {boolean} True if an object is a function. */ isFunction( object ) { return !! ( object && object.constructor && object.call && object.apply ); }, /** * Compare times. * * @since 1.7.1 * * @param {string} time1 Time 1. * @param {string} time2 Time 2. * * @return {boolean} True if time1 is greater than time2. */ compareTimesGreaterThan( time1, time2 ) { // Proper format time: add space before AM/PM, make uppercase. time1 = time1.replace( /(am|pm)/g, ' $1' ).toUpperCase(); time2 = time2.replace( /(am|pm)/g, ' $1' ).toUpperCase(); const time1Date = Date.parse( '01 Jan 2021 ' + time1 ), time2Date = Date.parse( '01 Jan 2021 ' + time2 ); return time1Date >= time2Date; }, /** * Determine whether the modern markup setting is enabled. * * @since 1.8.1 * * @return {boolean} True if modern markup is enabled. */ isModernMarkupEnabled() { return !! wpforms_settings.isModernMarkupEnabled; }, /** * Initialize token updater. * * Maybe update token via AJAX if it looks like outdated. * * @since 1.8.8 */ initTokenUpdater() { // Attach event handler to all forms with class `wpforms-form` $( '.wpforms-form' ).on( 'focusin', function( event ) { const $form = $( event.target.closest( 'form' ) ); const timestamp = Date.now(); if ( ! this.needsTokenUpdate( timestamp, $form ) ) { return; } this.updateToken( timestamp, $form, event ); }.bind( this ) ); // Bind `this` to maintain context inside the function }, /** * Check if the form needs a new token. * * @param {number} timestamp Timestamp. * @param {jQuery} $form Form. * * @return {boolean} Whether token needs update or not. * * @since 1.8.9 */ needsTokenUpdate( timestamp, $form ) { const tokenTime = $form.attr( 'data-token-time' ) || 0; const diff = timestamp - ( tokenTime * 1000 ); // Check if the token is expired. return diff >= wpforms_settings.token_cache_lifetime * 1000 && ! this.isUpdatingToken; }, /** * Update the token for the form. * * @param {number} timestamp Timestamp. * @param {jQuery} $form Form. * @param {Event} event Event. * * @since 1.8.9 */ updateToken( timestamp, $form, event ) { const formId = $form.data( 'formid' ); const $submitBtn = $form.find( '.wpforms-submit' ); this.isUpdatingToken = true; $submitBtn.prop( 'disabled', true ); $.post( wpforms_settings.ajaxurl, { action: 'wpforms_get_token', formId, } ).done( function( response ) { if ( response.success ) { $form.attr( 'data-token-time', timestamp ); $form.attr( 'data-token', response.data.token ); // Re-enable the 'submit' button. $submitBtn.prop( 'disabled', false ); // Trigger form submission if the focus was on the 'submit' button. if ( event.target === $submitBtn[ 0 ] ) { $submitBtn.trigger( 'click' ); } } else { // eslint-disable-next-line no-console console.error( 'Failed to update token: ', response ); } } ).fail( function( jqXHR, textStatus, errorThrown ) { // eslint-disable-next-line no-console console.error( 'AJAX request failed: ', textStatus, errorThrown ); } ).always( function() { this.isUpdatingToken = false; // Re-enable the 'submit' button. $submitBtn.prop( 'disabled', false ); }.bind( this ) ); }, /** * Restore Submit button on Mobile. * * @since 1.8.9 */ restoreSubmitButtonOnEventPersisted() { window.onpageshow = function( event ) { // If back/forward button has been clicked, restore submit button for all forms on the page. if ( event.persisted ) { $( '.wpforms-form' ).each( function() { const $form = $( this ); app.restoreSubmitButton( $form, $form.closest( '.wpforms-container' ) ); } ); } }; }, /** * We need separate method for loading validation groups * because we may dynamically extend them. * * @since 1.9.2.3 * * @param {jQuery} $context Form element or some container inside specific form. */ loadValidationGroups( $context ) { const validator = $context.closest( '.wpforms-form' ).data( 'validator' ); if ( ! validator ) { return; } $.extend( validator.groups, app.getDateTimeValidationGroups( $context ) ); }, /** * Return validation groups for Date / Time field with * dropdown and there should only one error message for whole field. * * @since 1.9.2.3 * * @param {jQuery} $context Container to search for Date/Time fields. * * @return {Object} Object with validation groups, e.g. { * "wpforms[fields][1][date][m]": "wpforms-198-field_1", * "wpforms[fields][1][date][d]": "wpforms-198-field_1" * "wpforms[fields][1][date][y]": "wpforms-198-field_1", * ... * } */ getDateTimeValidationGroups( $context ) { const groups = {}; // Create groups for the Date / Time field. $context.find( '.wpforms-field.wpforms-field-date-time' ).each( function() { const $field = $( this ); // Bail out if the date dropdown is NOT used for this field. if ( ! $field.find( '.wpforms-field-date-dropdown-wrap' ).length ) { return; } // e.g. wpforms-198-field_1 const groupName = $field.attr( 'id' ).replace( '-container', '' ); $.each( [ 'month', 'day', 'year' ], function( i, subfield ) { const $subfield = $( `#${ groupName }-${ subfield }` ); const subFieldName = $subfield.attr( 'name' ); groups[ subFieldName ] = groupName; } ); } ); return groups; }, /** * Retrieve current timestamp in seconds. * * @since 1.9.2.3 * * @return {number} Current timestamp in seconds. */ getTimestampSec() { return Math.floor( Date.now() / 1000 ); }, }; return app; }( document, window, jQuery ) ); // Initialize. wpforms.init(); frontend/wpforms.min.js000064400000135533151716470030011216 0ustar00var wpforms=window.wpforms||function(s,l,p){const m={cache:{},isUpdatingToken:!1,init(){p(m.ready),p(l).on("load",function(){"function"==typeof p.ready.then?p.ready.then(m.load):m.load()}),m.bindUIActions(),m.bindOptinMonster()},ready(){m.clearUrlQuery(),m.setUserIdentifier(),m.loadValidation(),m.loadHoneypot(),m.loadDatePicker(),m.loadTimePicker(),m.loadInputMask(),m.loadPayments(),m.loadMailcheck(),m.loadChoicesJS(),m.initTokenUpdater(),m.restoreSubmitButtonOnEventPersisted(),m.bindChoicesJS(),p(".wpforms-randomize").each(function(){for(var e=p(this),t=e.children();t.length;)e.append(t.splice(Math.floor(Math.random()*t.length),1)[0])}),p(".wpforms-page-button").prop("disabled",!1),m.initFormsStartTime(),p(s).trigger("wpformsReady")},load(){},clearUrlQuery(){var e=l.location;let t=e.search;-1!==t.indexOf("wpforms_form_id=")&&(t=t.replace(/([&?]wpforms_form_id=[0-9]*$|wpforms_form_id=[0-9]*&|[?&]wpforms_form_id=[0-9]*(?=#))/,""),history.replaceState({},null,e.origin+e.pathname+t))},loadHoneypot(){p(".wpforms-form").each(function(){const e=p(this),t=e.data("formid"),r=[],a=[];var o,i,s,n;void 0!==wpforms_settings.hn_data[t]&&(p(`#wpforms-form-${t} .wpforms-field`).each(function(){var e=p(this);r.push(e.data("field-id")),a.push(e.find(".wpforms-field-label").text())}),o=m.getHoneypotRandomLabel(a.join(" ").split(" ")),i=m.getHoneypotFieldId(r),n=r[Math.floor(Math.random()*r.length)],s=`wpforms-${t}-field_`+i,p(`#wpforms-${t}-field_${n}-container`,e).before(`
    `),(n=p(`#wpforms-${t}-field_${wpforms_settings.hn_data[t]}-container`,e)).find("input").attr({tabindex:"-1","aria-hidden":"true"}),n.find("label").text(o).attr("aria-hidden","true"))})},getHoneypotRandomLabel(t){let r="";for(let e=0;e<3;e++)r+=t[Math.floor(Math.random()*t.length)]+" ";return r.trim()},getHoneypotFieldId(t){var r=Math.max(...t);let a=0;for(let e=1;ea)return!1;return!0},wpforms_settings.val_filesize),p.validator.addMethod("step",function(e,t,r){i=r;const a=Math.floor(i)!==i&&i.toString().split(".")[1].length||0;function o(e){return Math.round(e*Math.pow(10,a))}var i=o(p(t).attr("min"));return e=o(e)-i,this.optional(t)||o(e)%o(r)==0}),p.validator.methods.email=function(e,t){return this.optional(t)||function(e){if("string"!=typeof e)return!1;var t=e.indexOf("@",1);if(e.length<6||254=Number(r.data("password-strength-level"))},wpforms_settings.val_password_strength),p(".wpforms-validate").each(function(){var e=p(this),t=e.data("formid");let r;r=void 0!==l["wpforms_"+t]&&l["wpforms_"+t].hasOwnProperty("validate")?l["wpforms_"+t].validate:"undefined"!=typeof wpforms_validate?wpforms_validate:{errorElement:m.isModernMarkupEnabled()?"em":"label",errorClass:"wpforms-error",validClass:"wpforms-valid",ignore:":hidden:not(textarea.wp-editor-area), .wpforms-conditional-hide textarea.wp-editor-area",ignoreTitle:!0,errorPlacement(e,t){m.isLikertScaleField(t)?(t.closest("table").hasClass("single-row")?t.closest(".wpforms-field"):t.closest("tr").find("th")).append(e):m.isWrappedField(t)?t.closest(".wpforms-field").append(e):m.isDateTimeField(t)?m.dateTimeErrorPlacement(t,e):m.isFieldInColumn(t)||m.isFieldHasHint(t)?t.parent().append(e):m.isLeadFormsSelect(t)?t.parent().parent().append(e):t.hasClass("wp-editor-area")?t.parent().parent().parent().append(e):e.insertAfter(t),m.isModernMarkupEnabled()&&e.attr({role:"alert","aria-label":wpforms_settings.errorMessagePrefix,for:""})},highlight(e,t,r){var a=p(e),o=a.closest(".wpforms-field"),i=a.attr("name");("radio"===a.attr("type")||"checkbox"===a.attr("type")?o.find('input[name="'+i+'"]'):a).addClass(t).removeClass(r),"password"===a.attr("type")&&""===a.val().trim()&&l.WPFormsPasswordField&&a.data("rule-password-strength")&&a.hasClass("wpforms-field-required")&&WPFormsPasswordField.passwordStrength("",e),o.addClass("wpforms-has-error")},unhighlight(e,t,r){var e=p(e),a=e.closest(".wpforms-field"),o=e.attr("name");("radio"===e.attr("type")||"checkbox"===e.attr("type")?a.find('input[name="'+o+'"]'):e).addClass(r).removeClass(t),a.find(":input.wpforms-error,[data-dz-errormessage]:not(:empty)").length||a.removeClass("wpforms-has-error"),m.isModernMarkupEnabled()&&e.parent().find("em.wpforms-error").remove()},submitHandler(s){function n(e,t){let r="label",a="";m.isModernMarkupEnabled()&&(r="em",a='role="alert"');var o=`<${r} id="wpforms-field_recaptcha-error" class="wpforms-error" ${a}> ${wpforms_settings.val_recaptcha_fail_msg}`;e.find(".wpforms-recaptcha-container").append(o),m.restoreSubmitButton(e,t)}function d(e){var t=e.find(".wpforms-submit");t.prop("disabled",!0),WPFormsUtils.triggerEvent(e,"wpformsFormSubmitButtonDisable",[e,t])}function e(){const e=p(s),t=e.closest(".wpforms-container"),r=e.find(".wpforms-submit"),a=r.data("captchaInvalid"),o=r.data("alt-text"),i=r.get(0).recaptchaID;return e.data("token")&&0===p(".wpforms-token",e).length&&p('').val(e.data("token")).appendTo(e),e.find("#wpforms-field_recaptcha-error").remove(),d(e),o&&r.text(o),a?n(e,t):m.empty(i)&&0!==i?(p(".wpforms-input-temp-name").removeAttr("name"),void m.formSubmit(e)):(grecaptcha.execute(i).then(null,function(){grecaptcha.getResponse()||n(e,t)}),!1)}return"function"==typeof wpformsRecaptchaV3Execute?(d(p(s)),wpformsRecaptchaV3Execute(e)):e()},invalidHandler(e,t){void 0!==t.errorList[0]&&m.scrollToError(p(t.errorList[0].element))},onkeyup:WPFormsUtils.debounce(function(e,t){p(e).hasClass("wpforms-novalidate-onkeyup")||9===t.which&&""===this.elementValue(e)||-1!==p.inArray(t.keyCode,[16,17,18,20,35,36,37,38,39,40,45,144,225])||(e.name in this.submitted||e.name in this.invalid)&&this.element(e)},1e3),onfocusout:function(e){let t=!1;p(e).hasClass("wpforms-novalidate-onkeyup")&&!e.value&&(t=!0),this.checkable(e)||!(e.name in this.submitted)&&this.optional(e)||(t=!0),(t=p(e).data("server-error")?!1:t)&&this.element(e)},onclick(e){let t=!1;var r=(e||{}).type;let a=p(e);-1<["checkbox","radio"].indexOf(r)&&((a=a.hasClass("wpforms-likert-scale-option")?a.closest("tr"):a.closest(".wpforms-field")).find("label.wpforms-error, em.wpforms-error").remove(),t=!0),t&&this.element(e)}},e.validate(r),m.loadValidationGroups(e)}))},restrictedEmailRequest(r,a){var e=p(r),t=e.closest("form");const o=t.data("validator"),i=t.data("formid");t=e.closest(".wpforms-field").data("field-id");m.cache[i]=m.cache[i]||{},o.startRequest(r),p.post({url:wpforms_settings.ajaxurl,type:"post",data:{action:"wpforms_restricted_email",form_id:i,field_id:t,email:a},dataType:"json",success(e){var t={},e=e.success&&e.data;e||(t[r.name]=wpforms_settings.val_email_restricted,o.showErrors(t)),m.cache[i].restrictedEmailValidation=m.cache[i].restrictedEmailValidation||[],Object.prototype.hasOwnProperty.call(m.cache[i].restrictedEmailValidation,a)||(m.cache[i].restrictedEmailValidation[a]=e),o.stopRequest(r,e)}})},isFieldInColumn(e){return e.parent().hasClass("wpforms-one-half")||e.parent().hasClass("wpforms-two-fifths")||e.parent().hasClass("wpforms-one-fifth")},isFieldHasHint(e){return 0e.toLowerCase()),e.preferredCountries=e.preferredCountries.map(e=>e.toLowerCase()),l.intlTelInput(t[0],e),t.siblings('input[type="hidden"]').each(function(){var e=p(this);e.attr("name",e.attr("name").replace("wpf-temp-",""))}))})},repairSmartPhoneHiddenField(e){console.warn('WARNING! Function "wpforms.repairSmartPhoneHiddenField()" has been deprecated, please use the new "WPFormsPhoneField.repairSmartHiddenField()" function instead!'),WPFormsPhoneField?.repairSmartHiddenField?.(e)},getDefaultSmartPhoneFieldOptions(){return console.warn('WARNING! Function "wpforms.getDefaultSmartPhoneFieldOptions()" has been deprecated, please use the new "WPFormsPhoneField.getDefaultSmartFieldOptions()" function instead!'),WPFormsPhoneField?.getDefaultSmartFieldOptions?.()},loadSmartPhoneField(e){console.warn('WARNING! Function "wpforms.loadSmartPhoneField()" has been deprecated, please use the new "WPFormsPhoneField.loadSmartField()" function instead!'),WPFormsPhoneField?.loadSmartField?.(e)},loadJqueryIntlTelInput(){console.warn('WARNING! Function "wpforms.loadJqueryIntlTelInput()" has been deprecated, please use the new "WPFormsPhoneField.loadJqueryIntlTelInput()" function instead!'),WPFormsPhoneField?.loadJqueryIntlTelInput?.()},initSmartPhoneField(e,t){console.warn('WARNING! Function "wpforms.initSmartPhoneField()" has been deprecated, please use the new "WPFormsPhoneField.initSmartField()" function instead!'),WPFormsPhoneField?.initSmartField?.(e,t)},bindSmartPhoneField(){console.warn('WARNING! Function "wpforms.bindSmartPhoneField()" has been deprecated, please use the new "WPFormsPhoneField.bindSmartField()" function instead!'),WPFormsPhoneField?.bindSmartField?.()},loadPayments(){p(".wpforms-payment-total").each(function(e,t){m.amountTotal(this)}),void 0!==p.fn.payment&&(p(".wpforms-field-credit-card-cardnumber").payment("formatCardNumber"),p(".wpforms-field-credit-card-cardcvc").payment("formatCardCVC"))},loadMailcheck(){wpforms_settings.mailcheck_enabled&&void 0!==p.fn.mailcheck&&(0'"()/\\|:;=@%&\s]/gi,"").substr(0,64),a=decodeURI(t.domain).replaceAll(/[<>'"()/\\|:;=@%&+_\s]/gi,"");t=''+r+"@"+a+"",t=wpforms_settings.val_email_suggestion.replace("{suggestion}",t),e.closest(".wpforms-field").find("#"+o+"_suggestion").remove(),e.parent().append('")},empty(){p("#"+o+"_suggestion").remove()}})}),p(s).on("click",".wpforms-field-email .mailcheck-suggestion",function(e){var t=p(this),r=t.closest(".wpforms-field"),a=t.data("id");e.preventDefault(),r.find("#"+a).val(t.text()),t.parent().remove()}))},loadChoicesJS(i){"function"==typeof l.Choices&&(i=i?.length?i:p(s)).find(".wpforms-field-select-style-modern .choicesjs-select, .wpforms-field-payment-select .choicesjs-select").each(function(e,t){var r,a,o;p(t).data("choicesjs")||WPFormsUtils.triggerEvent(i,"wpformsBeforeLoadElementChoices",[t]).isDefaultPrevented()||(r=l.wpforms_choicesjs_config||{},a=p(t).data("search-enabled"),o=p(t).data("remove-items-enabled"),r.searchEnabled=void 0===a||a,r.removeItems=void 0===o||o,r.removeItemButton=r.removeItems,r.searchEnabled=void 0===a||a,r.allowHTML=!0,r.callbackOnInit=function(){const t=this,r=p(t.passedElement.element),a=p(t.input.element),e=r.data("size-class");r.removeAttr("hidden").addClass(t.config.classNames.input+"--hidden"),e&&p(t.containerOuter.element).addClass(e),r.prop("multiple")&&(a.data("placeholder",a.attr("placeholder")).css("width","auto"),t.getValue(!0).length&&a.removeAttr("placeholder"),a.css("width","1ch")),r.on("change",function(){r.prop("multiple")&&(t.getValue(!0).length?a.removeAttr("placeholder"):a.attr("placeholder",a.data("placeholder")).css("width","auto"));var e=r.closest("form").data("validator");e&&e.element(r)})},r.callbackOnCreateTemplates=function(){const r=p(this.passedElement.element);return{option(e){var t=Choices.defaults.templates.option.call(this,e);return void 0!==e.placeholder&&!0===e.placeholder&&t.classList.add("placeholder"),r.hasClass("wpforms-payment-price")&&void 0!==e.customProperties&&null!==e.customProperties&&(t.dataset.amount=e.customProperties),t}}},p(t).data("choicesjs",new Choices(t,r)))})},bindChoicesJS(){p(s).on("click",".choices",function(e){var t=p(this),r=t.find("select").data("choicesjs");r&&t.hasClass("is-open")&&(e.target.classList.contains("choices__inner")||e.target.classList.contains("choices__arrow"))&&r.hideDropdown()})},bindUIActions(){var e=p(s);e.on("click",".wpforms-page-button",function(e){e.preventDefault(),m.pagebreakNav(this)});let t;e.on("change input",".wpforms-payment-price",function(){clearTimeout(t),t=setTimeout(()=>{m.amountTotal(this,!0)},0)});let r;e.on("change input","select.wpforms-payment-quantity",function(){clearTimeout(r),r=setTimeout(()=>{m.amountTotal(this,!0),m.updateOrderSummaryItemQuantity(p(this))},0)}),e.on("input",".wpforms-payment-user-input",function(){var e=p(this),t=e.val();e.val(t.replace(/[^0-9.,]/g,""))}),e.on("focusout",".wpforms-payment-user-input",function(){var e=p(this),t=e.val();if(!t)return t;t=m.amountSanitize(t),t=m.amountFormat(t);e.val(t)});let a;e.on("wpformsProcessConditionals",function(e,t){clearTimeout(a),a=setTimeout(()=>{m.amountTotal(t,!0)},0)}),e.on("mouseenter",".wpforms-field-rating-item",function(){p(this).parent().find(".wpforms-field-rating-item").removeClass("selected hover"),p(this).prevAll().addBack().addClass("hover")}).on("mouseleave",".wpforms-field-rating-item",function(){p(this).parent().find(".wpforms-field-rating-item").removeClass("selected hover"),p(this).parent().find("input:checked").parent().prevAll().addBack().addClass("selected")}),p(s).on("change",".wpforms-field-rating-item input",function(){var e=p(this),t=e.closest(".wpforms-field-rating-items").find(".wpforms-field-rating-item");e.focus(),t.removeClass("hover selected"),e.parent().prevAll().addBack().addClass("selected")}),p(function(){p(".wpforms-field-rating-item input:checked").trigger("change")}),e.on("keydown",".wpforms-image-choices-item label",function(e){var t=p(this);if(t.closest(".wpforms-field").hasClass("wpforms-conditional-hide"))return e.preventDefault(),!1;32===e.keyCode&&(t.find("input").trigger("click"),e.preventDefault())}),l.document.documentMode&&e.on("click",".wpforms-image-choices-item img",function(){p(this).closest("label").find("input").trigger("click")}),e.on("change",".wpforms-field-checkbox input, .wpforms-field-radio input, .wpforms-field-payment-multiple input, .wpforms-field-payment-checkbox input, .wpforms-field-gdpr-checkbox input",function(e){var t=p(this);if(t.closest(".wpforms-field").hasClass("wpforms-conditional-hide"))return e.preventDefault(),!1;switch(t.attr("type")){case"radio":t.closest("ul").find("li").removeClass("wpforms-selected").find("input[type=radio]").removeProp("checked"),t.prop("checked",!0).closest("li").addClass("wpforms-selected");break;case"checkbox":t.is(":checked")?(t.closest("li").addClass("wpforms-selected"),t.prop("checked",!0)):(t.closest("li").removeClass("wpforms-selected"),t.prop("checked",!1))}}),e.on("input",".wpforms-field-file-upload",function(){var e=p(this),t=e.closest("form.wpforms-form").find('.wpforms-field-file-upload input:not(".dropzone-input")');let a=0,r=Number(wpforms_settings.post_max_size),o='
    '+wpforms_settings.val_post_max_size+"
    ";e=e.closest("form.wpforms-form").find(".wpforms-submit-container");let i=e.find("button.wpforms-submit"),s=e.prev();var n=i.closest("form"),d=n.find(".wpforms-page-next:visible");0!==n.find(".wpforms-page-indicator").length&&0!==d.length&&(i=d),t.each(function(){var e=p(this);let t=0;for(var r=e[0].files.length;t{errorMsg}
    '.replace(/{errorMsg}/,o)),s=e.prev()),i.prop("disabled",!0),WPFormsUtils.triggerEvent(n,"wpformsFormSubmitButtonDisable",[n,i]),WPFormsUtils.triggerEvent(n,"wpformsCombinedUploadsSizeError",[n,s]))}),e.on("change input",".wpforms-field-number-slider input[type=range]",function(e){var t=p(e.target).siblings(".wpforms-field-number-slider-hint");t.html(t.data("hint").replaceAll("{value}",""+e.target.value+""))}),e.on("keydown",".wpforms-form input",function(e){var t,r;13!==e.keyCode||0===(r=(t=p(this)).closest(".wpforms-page")).length||["text","tel","number","email","url","radio","checkbox"].indexOf(t.attr("type"))<0||(t.hasClass("wpforms-datepicker")&&t.flatpickr("close"),e.preventDefault(),(r.hasClass("last")?r.closest(".wpforms-form").find(".wpforms-submit"):r.find(".wpforms-page-next")).trigger("click"))}),e.on("keypress",".wpforms-field-number input",function(e){return/^[-0-9.]+$/.test(String.fromCharCode(e.keyCode||e.which))}),e.one("input",".wpforms-field input, .wpforms-field textarea, .wpforms-field select",m.formChanged).one("change",".wpforms-field-select-style-modern, .wpforms-timepicker",m.formChanged).one("focus",".dropzone-input",m.formChanged).one("click touchstart",".wpforms-signature-canvas",m.formChanged).one("wpformsRichTextContentChange",m.richTextContentChanged),p("form.wpforms-form").on("wpformsBeforePageChange",m.skipEmptyPages)},skipEmptyPages(e,t,r,a){var o=m.findNonEmptyPage(t,r,a);o!==t&&(e.preventDefault(),1===o&&"prev"===a?(e=r.find(".wpforms-page-2"),a=r.find(".wpforms-page-"+t),t=(e.find(".wpforms-page-prev").length?e:a).find(".wpforms-page-prev"),wpforms.navigateToPage(t,"prev",2,r,e)):(t=r.find(".wpforms-page-"+(a=o-1)),wpforms.navigateToPage(t.find(".wpforms-page-next"),"next",a,r,t)))},findNonEmptyPage(e,t,r){let a=e;for(;m.isEmptyPage(t,a);)"prev"===r?a--:a++;return a},isEmptyPage(e,t){return 1!==t&&!(e=e.find(".wpforms-page-"+t)).hasClass("last")&&(t=e.find(".wpforms-field:not(.wpforms-field-pagebreak):not(.wpforms-field-hidden)"),e.find(".wpforms-conditional-hide").length===t.length)},formChanged(e){var t=p(this).closest(".wpforms-form");m.maybeSetStartTime(t)},richTextContentChanged(e,t,r){r=r.getContainer(),r=p(r).closest(".wpforms-form");m.maybeSetStartTime(r)},initFormsStartTime(){p(".wpforms-form").each(function(){m.maybeSetStartTime(p(this))})},maybeSetStartTime(e){e.data("start_timestamp")||e.data("start_timestamp",m.getTimestampSec())},entryPreviewFieldPageChange(e,t,r){console.warn("WARNING! Obsolete function called. Function wpforms.entryPreviewFieldPageChange has been deprecated, please use the WPFormsEntryPreview.pageChange function instead!"),WPFormsEntryPreview.pageChange(e,t,r)},entryPreviewFieldUpdate(e,t){console.warn("WARNING! Obsolete function called. Function wpforms.entryPreviewFieldUpdate has been deprecated, please use the WPFormsEntryPreview.update function instead!"),WPFormsEntryPreview.update(e,t)},scrollToError(e){if(0!==e.length){let t=e.find(".wpforms-field.wpforms-has-error");0!==(t=0===t.length?e.closest(".wpforms-field"):t).length&&void 0!==(e=t.offset())&&m.animateScrollTop(e.top-75,750).done(function(){var e=t.find(".wpforms-error").first();"function"==typeof e.focus&&e.trigger("focus")})}},pagebreakNav(e){const t=p(e),r=t.data("action"),a=t.data("page"),o=t.closest(".wpforms-form"),i=o.find(".wpforms-page-"+a);m.saveTinyMCE(),"next"===r&&void 0!==p.fn.validate?m.checkForInvalidFields(o,i,function(){m.navigateToPage(t,r,a,o,i)}):"prev"!==r&&"next"!==r||m.navigateToPage(t,r,a,o,i)},checkForInvalidFields(e,t,r){var o=e.data("validator");if(o){let a=!0;t.find(":input").each(function(e,t){var r=p(t);!r.attr("name")||r.hasClass("wpforms-field-skip-validation")||p(t).valid()||(a=!1)}),0
    '),i.render(o,{sitekey:a,callback(){wpformsRecaptchaCallback(p("#"+o))}})}},amountTotal(e,t){t=t||!1;const r=p(e),a=r.closest(".wpforms-form"),o=m.amountTotalCalc(a);if(m.allowAmountTotalCalc(a,r,o)){const i=m.amountFormatSymbol(o);a.find(".wpforms-payment-total").each(function(){"hidden"===p(this).attr("type")||"text"===p(this).attr("type")?(p(this).val(i),"text"===p(this).attr("type")&&t&&a.data("validator")&&p(this).valid()):p(this).text(i)}),m.updateOrderSummaryItems(a,r,i)}},allowAmountTotalCalc(e,t,r){var e=e.data("formid");return m.getCache(e,"amountTotal")!==r?(m.updateCache(e,"amountTotal",r),!0):"radio"===(e=t.prop("type"))||"select-one"===e||"checkbox"===e},updateOrderSummaryItems(t,e,r){t.find(".wpforms-order-summary-preview").each(function(){const e=p(this);""!==r&&e.find(".wpforms-order-summary-preview-total .wpforms-order-summary-item-price").text(r),t.find(".wpforms-payment-price").each(function(){m.updateOrderSummaryItem(p(this),e)})})},updateCache(e,t,r){m.cache[e]=m.cache[e]||{},m.cache[e][t]=r},getCache(e,t){return!(!Object.prototype.hasOwnProperty.call(m.cache,e)||!Object.prototype.hasOwnProperty.call(m.cache[e],t))&&m.cache[e][t]},updateOrderSummaryItem(e,t){if(e.hasClass("wpforms-payment-price")){const o=e.closest(".wpforms-field"),i=o.data("field-id"),s=e.prop("type"),n="block"===o.css("display");var r,a;"checkbox"===s||"radio"===s||"select-one"===s?t.find(`tr[data-field="${i}"]`).each(function(){var e=p(this).data("choice"),e="select-one"===s?e===parseInt(o.find("select").val(),10):o.find(`input[value="${e}"]`).is(":checked");p(this).toggle(n&&e)}):(r=t.find(`tr[data-field="${i}"]`),a=e.val(),r.find(".wpforms-order-summary-item-price").text(m.amountFormatSymbol(m.amountSanitize(a))),r.toggle(n)),o.hasClass("wpforms-payment-quantities-enabled")?m.updateOrderSummaryItemQuantity(e):(m.updateSummaryPriceWidth(t),m.toggleSummaryPlaceholder(t))}},updateOrderSummaryItemQuantity(e){const t=e.closest(".wpforms-field"),a=t.find("input.wpforms-payment-price, select.wpforms-payment-price"),r=e.closest(".wpforms-form"),o=t.data("field-id"),i=m.getPaymentFieldQuantity(a),s=m.getPaymentFieldAmount(a),n=a.prop("type");r.find(".wpforms-order-summary-preview").each(function(){var e,t=p(this);let r;(r="checkbox"===n||"radio"===n||"select-one"===n?(e=a.val(),t.find(`tr[data-field="${o}"][data-choice="${e}"]`)):t.find(`tr[data-field="${o}"]`)).toggle(0'),e.append(''),e.get(0).submit())},formHasCaptcha(e){return!(!e||!e.length||"undefined"==typeof hcaptcha&&"undefined"==typeof grecaptcha&&"undefined"==typeof turnstile)&&(e=e.find(".wpforms-recaptcha-container"),Boolean(e.length))},resetFormRecaptcha(r){if(m.formHasCaptcha(r)){var a=r.find(".wpforms-recaptcha-container");let e,t;e=a.hasClass("wpforms-is-hcaptcha")?hcaptcha:a.hasClass("wpforms-is-turnstile")?turnstile:grecaptcha,t=r.find(".wpforms-submit").get(0).recaptchaID,m.empty(t)&&0!==t&&(t=r.find(".g-recaptcha").data("recaptcha-id")),m.empty(t)&&0!==t||e.reset(t)}},consoleLogAjaxError(e){e?console.error("WPForms AJAX submit error:\n%s",e):console.error("WPForms AJAX submit error")},displayFormAjaxErrors(e,t){"string"==typeof t?m.displayFormAjaxGeneralErrors(e,t):(t=t&&"errors"in t?t.errors:null,m.empty(t)||m.empty(t.general)&&m.empty(t.field)?m.consoleLogAjaxError():(m.empty(t.general)||m.displayFormAjaxGeneralErrors(e,t.general),m.empty(t.field)||m.displayFormAjaxFieldErrors(e,t.field)))},displayFormAjaxGeneralErrors(e,t){var r,a;e&&e.length&&(m.empty(t)||(m.isModernMarkupEnabled()&&e.attr({"aria-invalid":"true","aria-errormessage":""}),"string"==typeof t?(a=m.isModernMarkupEnabled()?' role="alert"':"",r=m.isModernMarkupEnabled()?`${wpforms_settings.formErrorMessagePrefix}`:"",e.find(".wpforms-submit-container").before(`
    ${r}${t}
    `),m.setCurrentPage(e,{})):(a=e.data("formid"),m.printGeneralErrors(e,t,a))))},printGeneralErrors(o,e,i){p.each(e,function(e,t){switch(e){case"header":case"header_styled":o.prepend(t);break;case"footer":case"footer_styled":r=t,0===o.find(".wpforms-page-indicator").length?o.find(".wpforms-submit-container").before(r):o.find(".wpforms-page-1").append(r);break;case"recaptcha":r=t,o.find(".wpforms-recaptcha-container").append(r)}var r,a;m.isModernMarkupEnabled()&&(a=o.attr("aria-errormessage")||"",o.attr("aria-errormessage",a+` wpforms-${i}-${e}-error`))}),o.find(".wpforms-error-container").length&&m.animateScrollTop(o.find(".wpforms-error-container").first().offset().top-100)},clearFormAjaxGeneralErrors(e){e.find(".wpforms-error-container").remove(),e.find("#wpforms-field_recaptcha-error").remove(),m.isModernMarkupEnabled()&&e.attr({"aria-invalid":"false","aria-errormessage":""})},displayFormAjaxFieldErrors(a,o){var e;a&&a.length&&(m.empty(o)||(e=a.data("validator"))&&(o=m.splitFieldErrors(o),p.each(o,function(e,t){var r=p('[name="'+e+'"]',a);r.length?r.attr("data-server-error",t):delete o[e]}),e.showErrors(o),m.formHasCaptcha(a)||e.focusInvalid()))},splitFieldErrors:i=>(p.each(i,function(o,e){"string"!=typeof e&&p.each(e,function(e,t){var r=o.split("[").pop().replace("]",""),a=o.replace("["+r+"]","");r===e?i[o]=t:"string"==typeof e&&isNaN(e)&&(i[a+"["+e+"]"]=t)})}),i),formSubmitAjax:a=>{if(!a.length)return p.Deferred().reject();const r=a.closest(".wpforms-container"),e=a.find(".wpforms-submit-spinner");let o;r.css("opacity",.6),e.show(),m.clearFormAjaxGeneralErrors(a);var t=new FormData(a.get(0)),t=(t.append("action","wpforms_submit"),t.append("start_timestamp",a.data("start_timestamp")),t.append("end_timestamp",m.getTimestampSec()),{type:"post",dataType:"json",url:wpforms_settings.ajaxurl,data:t,cache:!1,contentType:!1,processData:!1});return t.success=function(e){var t;if(e)if(e.data&&e.data.action_required)a.trigger("wpformsAjaxSubmitActionRequired",e);else if(e.success){if(a.trigger("wpformsAjaxSubmitSuccess",e),e.data)return e.data.redirect_url?(t=e.data.new_tab||!1,a.trigger("wpformsAjaxSubmitBeforeRedirect",e),t?(l.open(e.data.redirect_url,"_blank"),void location.reload()):void(l.location=e.data.redirect_url)):void(e.data.confirmation&&(r.html(e.data.confirmation),o=r.find("div.wpforms-confirmation-scroll"),r.trigger("wpformsAjaxSubmitSuccessConfirmation",e),o.length)&&m.animateScrollTop(o.offset().top-100))}else m.resetFormRecaptcha(a),m.displayFormAjaxErrors(a,e.data),a.trigger("wpformsAjaxSubmitFailed",e),m.setCurrentPage(a,e.data);else m.consoleLogAjaxError()},t.error=function(e,t,r){m.consoleLogAjaxError(r),a.trigger("wpformsAjaxSubmitError",[e,t,r])},t.complete=function(e,t){e.responseJSON&&e.responseJSON.data&&(e.responseJSON.data.action_required||"success"===t&&e.responseJSON.data.redirect_url)||(m.restoreSubmitButton(a,r),a.trigger("wpformsAjaxSubmitCompleted",[e,t]))},WPFormsUtils.triggerEvent(a,"wpformsAjaxBeforeSubmit",[a]).isDefaultPrevented()?(m.restoreSubmitButton(a,r),p.Deferred().reject()):p.ajax(t)},setCurrentPage(r,a){if(0!==r.find(".wpforms-page-indicator").length){const i=[];if(r.find(".wpforms-page").each(function(e,t){if(1<=p(t).find(".wpforms-has-error").length)return i.push(p(t))}),0!==i.length||void 0===a.errors||void 0===a.errors.general||void 0!==a.errors.general.footer||void 0===a.errors.general.recaptcha){var o=0=1e3*wpforms_settings.token_cache_lifetime&&!this.isUpdatingToken},updateToken(t,r,a){var e=r.data("formid");const o=r.find(".wpforms-submit");this.isUpdatingToken=!0,o.prop("disabled",!0),p.post(wpforms_settings.ajaxurl,{action:"wpforms_get_token",formId:e}).done(function(e){e.success?(r.attr("data-token-time",t),r.attr("data-token",e.data.token),o.prop("disabled",!1),a.target===o[0]&&o.trigger("click")):console.error("Failed to update token: ",e)}).fail(function(e,t,r){console.error("AJAX request failed: ",t,r)}).always(function(){this.isUpdatingToken=!1,o.prop("disabled",!1)}.bind(this))},restoreSubmitButtonOnEventPersisted(){l.onpageshow=function(e){e.persisted&&p(".wpforms-form").each(function(){var e=p(this);m.restoreSubmitButton(e,e.closest(".wpforms-container"))})}},loadValidationGroups(e){var t=e.closest(".wpforms-form").data("validator");t&&p.extend(t.groups,m.getDateTimeValidationGroups(e))},getDateTimeValidationGroups(e){const a={};return e.find(".wpforms-field.wpforms-field-date-time").each(function(){var e=p(this);if(e.find(".wpforms-field-date-dropdown-wrap").length){const r=e.attr("id").replace("-container","");p.each(["month","day","year"],function(e,t){t=p(`#${r}-`+t).attr("name");a[t]=r})}}),a},getTimestampSec(){return Math.floor(Date.now()/1e3)}};return m}(document,window,jQuery);wpforms.init();integrations/ai/chat-element/modules/api.js000064400000006426151716470030015004 0ustar00/* global wpforms_ai_chat_element */ /** * @param wpforms_ai_chat_element.ajaxurl * @param wpforms_ai_chat_element.errors.network * @param wpforms_ai_chat_element.errors.default */ /** * The WPForms AI API wrapper. * * @since 1.9.1 * * @return {Function} The app cloning function. */ export default function() { // eslint-disable-line no-unused-vars, max-lines-per-function /** * Public functions and properties. * * @since 1.9.1 * * @type {Object} */ const app = { /** * AI chat mode. * * @since 1.9.1 * * @type {string} */ mode: '', /** * AI AJAX actions. * * @since 1.9.1 * * @type {Object} */ actions: { rate: 'wpforms_rate_ai_response', choices: 'wpforms_get_ai_choices', forms: 'wpforms_get_ai_form', }, /** * AJAX request. * * @param {Object} data Data to send. * * @return {Promise} The fetch result data promise. */ // eslint-disable-next-line complexity async ajax( data ) { if ( ! data.nonce ) { data.nonce = wpforms_ai_chat_element.nonce; } const options = { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams( data ).toString(), }; const response = await fetch( wpforms_ai_chat_element.ajaxurl, options ) .catch( ( error ) => { if ( error.message === 'Failed to fetch' ) { throw new Error( wpforms_ai_chat_element.errors.network ); } else { throw new Error( error.message ); } } ); if ( ! response.ok ) { throw new Error( wpforms_ai_chat_element.errors.network ); } const result = await response.json(); if ( ! result.success || result.data?.error ) { throw new Error( result.data?.error ?? wpforms_ai_chat_element.errors.default, { cause: result.data?.code ?? 400, } ); } return result.data; }, /** * Prompt. * * @param {string} prompt The question to ask. * @param {string} sessionId Session ID. * * @return {Promise} The response data in promise. */ async prompt( prompt, sessionId ) { const data = { action: app.actions[ this.mode ] ?? app.actions.choices, prompt, }; if ( sessionId ) { data.session_id = sessionId; // eslint-disable-line camelcase } return app.ajax( data ); }, /** * Rate. * * @param {boolean} helpful Whether the response was helpful or not. * @param {string} responseId Response ID. * * @return {Promise} The response data in promise. */ async rate( helpful, responseId ) { const data = { action: app.actions.rate, helpful, response_id: responseId, // eslint-disable-line camelcase }; return app.ajax( data ); }, setUp() { app.actions = { ...app.actions, ...wpforms_ai_chat_element.actions, }; return this; }, /** * Set the AI chat mode. * * @since 1.9.1 * * @param {string} mode The mode to set. * * @return {Object} The app object. */ setMode( mode ) { this.mode = mode; return this; }, }; /** * Return a clone of an app object. * * @since 1.9.1 * * @param {string} mode The AI prompt mode. * * @return {Object} Cloned app object. */ return function( mode ) { const obj = { ...app }; return obj.setUp().setMode( mode ); }; } integrations/ai/chat-element/modules/api.min.js000064400000002077151716470030015564 0ustar00export default function(){const t={mode:"",actions:{rate:"wpforms_rate_ai_response",choices:"wpforms_get_ai_choices",forms:"wpforms_get_ai_form"},async ajax(e){e.nonce||(e.nonce=wpforms_ai_chat_element.nonce);e={method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(e).toString()},e=await fetch(wpforms_ai_chat_element.ajaxurl,e).catch(e=>{throw"Failed to fetch"===e.message?new Error(wpforms_ai_chat_element.errors.network):new Error(e.message)});if(!e.ok)throw new Error(wpforms_ai_chat_element.errors.network);e=await e.json();if(!e.success||e.data?.error)throw new Error(e.data?.error??wpforms_ai_chat_element.errors.default,{cause:e.data?.code??400});return e.data},async prompt(e,r){e={action:t.actions[this.mode]??t.actions.choices,prompt:e};return r&&(e.session_id=r),t.ajax(e)},async rate(e,r){e={action:t.actions.rate,helpful:e,response_id:r};return t.ajax(e)},setUp(){return t.actions={...t.actions,...wpforms_ai_chat_element.actions},this},setMode(e){return this.mode=e,this}};return function(e){return{...t}.setUp().setMode(e)}}integrations/ai/chat-element/modules/helpers-choices.js000064400000020552151716470030017304 0ustar00/* global WPFormsAIChatHTMLElement, WPFormsBuilder, wpf, wpforms_builder */ /** * The WPForms AI chat element. * * Choices helpers module. * * @since 1.9.1 * * @param {WPFormsAIChatHTMLElement} chat The chat element. * * @return {Object} The choices' helpers object. */ export default function( chat ) { // eslint-disable-line max-lines-per-function /** * The `choices` mode helpers object. * * @since 1.9.1 */ return { /** * Get the `choices` answer based on AI response data. * * @since 1.9.1 * * @param {Object} response The response data. * * @return {string} Answer HTML markup. */ getAnswer( response ) { if ( response.choices?.length < 1 ) { return ''; } const li = []; for ( const i in response.choices ) { li.push( `
  • ${ chat.htmlSpecialChars( response.choices[ i ] ) }
  • ` ); } let answerHtml = `

    ${ chat.htmlSpecialChars( response.heading ?? '' ) }

      ${ li.join( '' ) }
    `; // Add footer to the first answer only. if ( ! chat.sessionId ) { answerHtml += `${ chat.modeStrings.footer }`; } return answerHtml; }, /** * Get the answer pre-buttons HTML markup. * * @since 1.9.1 * * @return {string} The answer pre-buttons HTML markup. */ getAnswerButtonsPre() { return ` `; }, /** * Get the warning message HTML markup. * * @since 1.9.1 * * @return {string} The warning message HTML markup. */ getWarningMessage() { // Trigger event before warning message insert. chat.triggerEvent( 'wpformsAIModalBeforeWarningMessageInsert', { fieldId: chat.fieldId } ); return `
    ${ chat.modeStrings.warning }
    `; }, /** * If the field has default choices, the welcome screen is active. * * @since 1.9.1 * * @return {boolean} True if the field has default choices, false otherwise. */ isWelcomeScreen() { const items = document.getElementById( `wpforms-field-option-row-${ chat.fieldId }-choices` ) .querySelectorAll( 'li input.label' ); if ( items.length === 1 && ! items[ 0 ].value.trim() ) { return true; } if ( items.length > 3 ) { return false; } const defaults = Object.values( chat.modeStrings.defaults ); for ( let i = 0; i < items.length; i++ ) { if ( ! defaults.includes( items[ i ].value ) ) { return false; } } return true; }, /** * Add the `choices` answer. * * @since 1.9.1 * * @param {HTMLElement} element The answer element. */ addedAnswer( element ) { const button = element.querySelector( '.wpforms-ai-chat-choices-insert' ); // Listen to the button click event. button?.addEventListener( 'click', this.insertButtonClick.bind( this ) ); }, /** * Sanitize response. * * @since 1.9.2 * * @param {Object} response The response data to sanitize. * * @return {Object} The sanitized response. */ sanitizeResponse( response ) { if ( ! Array.isArray( response?.choices ) ) { return response; } let choices = response.choices; // Sanitize choices. choices = choices.map( ( choice ) => { return wpf.sanitizeHTML( choice, wpforms_builder.allowed_label_html_tags ); } ); // Remove empty choices. response.choices = choices.filter( ( choice ) => { return choice.trim() !== ''; } ); return response; }, /** * Check if the response has a prohibited code. * * @since 1.9.2 * * @param {Object} response The response data. * @param {Array} sanitizedResponse The sanitized response data. * * @return {boolean} Whether the answer has a prohibited code. */ hasProhibitedCode( response, sanitizedResponse ) { // If the number of choices has changed after sanitization, it means that the answer contains prohibited code. return sanitizedResponse?.choices?.length !== response?.choices?.length; }, /** * Click on the Use Choices button. * * @since 1.9.1 * * @param {Event} e The event object. */ insertButtonClick( e ) { const button = e.target; const answer = button.closest( '.wpforms-chat-item.wpforms-chat-item-choices' ); const responseId = answer?.getAttribute( 'data-response-id' ); const choicesList = answer?.querySelector( 'ol' ); const items = choicesList.querySelectorAll( '.wpforms-ai-chat-choices-item' ); const choiceItems = []; // Get choices data. for ( const i in items ) { if ( ! items.hasOwnProperty( i ) || ! items[ i ].textContent ) { continue; } choiceItems.push( items[ i ].textContent.trim() ); } // Rate the response. chat.wpformsAiApi.rate( true, responseId ); // Replace field choices. this.replaceChoices( choiceItems ); }, /** * Replace field choices. * * @since 1.9.1 * * @param {Array} choices Choices array. */ replaceChoices( choices ) { const choicesOptionRow = document.getElementById( `wpforms-field-option-row-${ chat.fieldId }-choices` ); const choicesList = choicesOptionRow.querySelector( 'ul.choices-list' ); const choiceRow = choicesList.querySelector( 'li:first-child' ).cloneNode( true ); choiceRow.innerHTML = choiceRow.innerHTML.replace( /\[choices\]\[\d+\]/g, `[choices][{{key}}]` ); // Clear existing choices. choicesList.innerHTML = ''; // Add new choices. for ( const i in choices ) { const key = ( Number( i ) + 1 ).toString(); const choice = choices[ i ]; // Clone choice item element. let li = choiceRow.cloneNode( true ); // Get updated single choice item. li = this.getUpdatedSingleChoiceItem( li, key, choice ); // Add new choice item. choicesList.appendChild( li ); } // Update data-next-id attribute for choices list. choicesList.setAttribute( 'data-next-id', choices.length + 1 ); // Update field preview. const fieldOptions = document.getElementById( `wpforms-field-option-${ chat.fieldId }` ); const fieldType = fieldOptions.querySelector( 'input.wpforms-field-option-hidden-type' )?.value; WPFormsBuilder.fieldChoiceUpdate( fieldType, chat.fieldId, choices.length ); WPFormsBuilder.triggerBuilderEvent( 'wpformsFieldChoiceAdd' ); // Trigger event after choices insert. chat.triggerEvent( 'wpformsAIModalAfterChoicesInsert', { fieldId: chat.fieldId } ); }, /** * Get updated single choice item. * * @since 1.9.1 * * @param {HTMLElement} li Choice item element. * @param {string} key Choice key. * @param {string} choice Choice value. * * @return {HTMLElement} The updated choice item. */ getUpdatedSingleChoiceItem( li, key, choice ) { li.setAttribute( 'data-key', key.toString() ); // Update choice item inputs name attributes. li.innerHTML = li.innerHTML.replaceAll( '{{key}}', key ); // Sanitize choice before set. choice = wpf.sanitizeHTML( choice ); const inputDefault = li.querySelector( 'input.default' ); inputDefault.removeAttribute( 'checked' ); // Set label const inputLabel = li.querySelector( 'input.label' ); inputLabel.value = choice; inputLabel.setAttribute( 'value', choice ); // Set value. const inputValue = li.querySelector( 'input.value' ); inputValue.value = choice; inputValue.setAttribute( 'value', choice ); // Reset image upload. const imageUpload = li.querySelector( '.wpforms-image-upload' ); const inputImage = imageUpload.querySelector( 'input.source' ); inputImage.value = ''; inputImage.setAttribute( 'value', '' ); imageUpload.querySelector( '.preview' ).innerHTML = ''; imageUpload.querySelector( '.wpforms-image-upload-add' ).style.display = 'block'; // Reset icon choice. const iconSelect = li.querySelector( '.wpforms-icon-select' ); iconSelect.querySelector( '.ic-fa-preview' ).setAttribute( 'class', 'ic-fa-preview ic-fa-regular ic-fa-face-smile' ); iconSelect.querySelector( 'input.source-icon' ).value = 'face-smile'; iconSelect.querySelector( 'input.source-icon-style' ).value = 'regular'; return li; }, }; } integrations/ai/chat-element/modules/helpers-choices.min.js000064400000006772151716470030020076 0ustar00export default function(c){return{getAnswer(e){if(e.choices?.length<1)return"";var t=[];for(const i in e.choices)t.push(`
  • ${c.htmlSpecialChars(e.choices[i])}
  • `);let r=`

    ${c.htmlSpecialChars(e.heading??"")}

      ${t.join("")}
    `;return c.sessionId||(r+=`${c.modeStrings.footer}`),r},getAnswerButtonsPre(){return` `},getWarningMessage(){return c.triggerEvent("wpformsAIModalBeforeWarningMessageInsert",{fieldId:c.fieldId}),`
    ${c.modeStrings.warning}
    `},isWelcomeScreen(){var t=document.getElementById(`wpforms-field-option-row-${c.fieldId}-choices`).querySelectorAll("li input.label");if(1!==t.length||t[0].value.trim()){if(3wpf.sanitizeHTML(e,wpforms_builder.allowed_label_html_tags)),t.choices=e.filter(e=>""!==e.trim())}return t},hasProhibitedCode(e,t){return t?.choices?.length!==e?.choices?.length},insertButtonClick(e){var e=e.target.closest(".wpforms-chat-item.wpforms-chat-item-choices"),t=e?.getAttribute("data-response-id"),r=(e?.querySelector("ol")).querySelectorAll(".wpforms-ai-chat-choices-item"),i=[];for(const o in r)r.hasOwnProperty(o)&&r[o].textContent&&i.push(r[o].textContent.trim());c.wpformsAiApi.rate(!0,t),this.replaceChoices(i)},replaceChoices(e){var t=document.getElementById(`wpforms-field-option-row-${c.fieldId}-choices`).querySelector("ul.choices-list"),r=t.querySelector("li:first-child").cloneNode(!0);r.innerHTML=r.innerHTML.replace(/\[choices\]\[\d+\]/g,"[choices][{{key}}]"),t.innerHTML="";for(const l in e){var i=(Number(l)+1).toString(),o=e[l],s=r.cloneNode(!0),s=this.getUpdatedSingleChoiceItem(s,i,o);t.appendChild(s)}t.setAttribute("data-next-id",e.length+1);var n=document.getElementById("wpforms-field-option-"+c.fieldId).querySelector("input.wpforms-field-option-hidden-type")?.value;WPFormsBuilder.fieldChoiceUpdate(n,c.fieldId,e.length),WPFormsBuilder.triggerBuilderEvent("wpformsFieldChoiceAdd"),c.triggerEvent("wpformsAIModalAfterChoicesInsert",{fieldId:c.fieldId})},getUpdatedSingleChoiceItem(e,t,r){e.setAttribute("data-key",t.toString()),e.innerHTML=e.innerHTML.replaceAll("{{key}}",t),r=wpf.sanitizeHTML(r);e.querySelector("input.default").removeAttribute("checked");t=e.querySelector("input.label"),t.value=r,t.setAttribute("value",r),t=e.querySelector("input.value"),t.value=r,t.setAttribute("value",r),t=e.querySelector(".wpforms-image-upload"),r=t.querySelector("input.source"),r.value="",r.setAttribute("value",""),t.querySelector(".preview").innerHTML="",t.querySelector(".wpforms-image-upload-add").style.display="block",r=e.querySelector(".wpforms-icon-select");return r.querySelector(".ic-fa-preview").setAttribute("class","ic-fa-preview ic-fa-regular ic-fa-face-smile"),r.querySelector("input.source-icon").value="face-smile",r.querySelector("input.source-icon-style").value="regular",e}}}integrations/ai/chat-element/modules/helpers-forms.js000064400000012657151716470030017024 0ustar00/* global WPFormsAIChatHTMLElement, WPFormsAIFormGenerator, wpf, wpforms_builder */ /** * @param chat.modeStrings.footerFirst * @param chat.modeStrings.inactiveAnswerTitle * @param chat.preventResizeInput * @param response.form_title * @param wpforms_builder.allowed_label_html_tags */ /** * The WPForms AI chat element. * * Forms mode helpers module. * * @since 1.9.2 * * @param {WPFormsAIChatHTMLElement} chat The chat element. * * @return {Object} Forms helpers object. */ export default function( chat ) { // eslint-disable-line no-unused-vars, max-lines-per-function /** * The input (textarea) height. * * @type {Object} */ const inputHeight = { min: 54, max: 95, }; /** * The default `forms` mode helpers object. * * @since 1.9.2 */ const forms = { /** * Init `forms` mode. * * @since 1.9.2 */ init() { chat.input.addEventListener( 'keydown', chat.modeHelpers.keyDown ); chat.input.addEventListener( 'keyup', chat.modeHelpers.resizeInput ); // Set the initial form generator state. if ( chat.sessionId ) { WPFormsAIFormGenerator.state.chatStart = true; // Remove the selected state from the current template card. WPFormsAIFormGenerator.main.el.$templateCard .next( '.selected' ).removeClass( 'selected' ); } }, /** * Detect the Enter key press. * Prevent resizing the input if Enter key pressed without Shift. * * @since 1.9.2 * * @param {KeyboardEvent} e The keyboard event. */ keyDown( e ) { chat.preventResizeInput = e.code === 'Enter' && ! e.shiftKey; if ( chat.preventResizeInput ) { e.preventDefault(); forms.setInputHeight( inputHeight.min ); } }, /** * Resize textarea while added new lines. * * @since 1.9.2 */ resizeInput() { if ( chat.preventResizeInput ) { return; } // Reset style to get the correct scroll height. chat.input.style.height = ''; chat.input.style.paddingTop = '10px'; chat.input.style.paddingBottom = '10px'; let height; const scrollHeight = chat.input.scrollHeight; // Calculate the height based on the scroll height. height = Math.min( scrollHeight, inputHeight.max ); height = Math.max( height, inputHeight.min ); forms.setInputHeight( height ); }, /** * Reset the message input field. * * @since 1.9.2 */ resetInput() { forms.resizeInput(); }, /** * Set textarea height. * * @since 1.9.2 * * @param {number} height The height. */ setInputHeight( height ) { // Adjust padding based on the height. if ( height <= inputHeight.min ) { chat.input.style.paddingTop = ''; chat.input.style.paddingBottom = ''; } // Set the height. chat.input.style.height = height + 'px'; chat.style.setProperty( '--wpforms-ai-chat-input-height', height + 'px' ); }, /** * Get the answer based on AI response data. * * @since 1.9.2 * * @param {Object} response The AI response data. * * @return {string} HTML markup. */ getAnswer( response ) { if ( ! response ) { return ''; } const rnd = Math.floor( Math.random() * chat.modeStrings.footer.length ); const footer = chat.modeStrings.footer[ rnd ]; const answer = response.explanation || ( response.form_title ?? '' ); return `

    ${ answer }

    ${ footer } `; }, /** * Get the answer pre-buttons HTML markup. * * @since 1.9.2 * * @return {string} The answer pre-buttons HTML markup. */ getAnswerButtonsPre() { return ` `; }, /** * The answer was added. * * @since 1.9.2 * * @param {HTMLElement} element The answer element. */ addedAnswer( element ) { // eslint-disable-line no-unused-vars forms.updateInactiveAnswers(); }, /** * Set active answer. * * @since 1.9.2 * * @param {HTMLElement} element The answer element. */ setActiveAnswer( element ) { forms.updateInactiveAnswers(); element.querySelector( '.wpforms-chat-item-content' ).setAttribute( 'title', '' ); }, /** * Update inactive answers. * * @since 1.9.2 */ updateInactiveAnswers() { chat.messageList.querySelectorAll( '.wpforms-chat-item-answer:not(.active) .wpforms-chat-item-content' ) .forEach( ( el ) => { // Set title attribute for inactive answers. el.setAttribute( 'title', chat.modeStrings.inactiveAnswerTitle ); } ); }, /** * Determine whether the Welcome Screen should be displayed. * * @since 1.9.2 * * @return {boolean} Display the Welcome Screen or not. */ isWelcomeScreen() { return true; }, /** * Get the message input field HTML. * * @since 1.9.2 * * @return {string} The message input field markup. */ getMessageInputField() { return ``; }, /** * Sanitize response. * * @since 1.9.2 * * @param {Object} response The response data to sanitize. * * @return {Object} The sanitized response. */ sanitizeResponse( response ) { if ( ! response.explanation ) { return response; } // Sanitize explanation string. response.explanation = wpf.sanitizeHTML( response.explanation, wpforms_builder.allowed_label_html_tags ); return response; }, }; return forms; } integrations/ai/chat-element/modules/helpers-forms.min.js000064400000003577151716470030017607 0ustar00export default function(n){const t={min:54,max:95},r={init(){n.input.addEventListener("keydown",n.modeHelpers.keyDown),n.input.addEventListener("keyup",n.modeHelpers.resizeInput),n.sessionId&&(WPFormsAIFormGenerator.state.chatStart=!0,WPFormsAIFormGenerator.main.el.$templateCard.next(".selected").removeClass("selected"))},keyDown(e){n.preventResizeInput="Enter"===e.code&&!e.shiftKey,n.preventResizeInput&&(e.preventDefault(),r.setInputHeight(t.min))},resizeInput(){var e;n.preventResizeInput||(n.input.style.height="",n.input.style.paddingTop="10px",n.input.style.paddingBottom="10px",e=n.input.scrollHeight,e=Math.min(e,t.max),e=Math.max(e,t.min),r.setInputHeight(e))},resetInput(){r.resizeInput()},setInputHeight(e){e<=t.min&&(n.input.style.paddingTop="",n.input.style.paddingBottom=""),n.input.style.height=e+"px",n.style.setProperty("--wpforms-ai-chat-input-height",e+"px")},getAnswer(e){var t;return e?(t=Math.floor(Math.random()*n.modeStrings.footer.length),t=n.modeStrings.footer[t],`

    ${e.explanation||(e.form_title??"")}

    ${t} `):""},getAnswerButtonsPre(){return` `},addedAnswer(e){r.updateInactiveAnswers()},setActiveAnswer(e){r.updateInactiveAnswers(),e.querySelector(".wpforms-chat-item-content").setAttribute("title","")},updateInactiveAnswers(){n.messageList.querySelectorAll(".wpforms-chat-item-answer:not(.active) .wpforms-chat-item-content").forEach(e=>{e.setAttribute("title",n.modeStrings.inactiveAnswerTitle)})},isWelcomeScreen(){return!0},getMessageInputField(){return``},sanitizeResponse(e){return e.explanation&&(e.explanation=wpf.sanitizeHTML(e.explanation,wpforms_builder.allowed_label_html_tags)),e}};return r}integrations/ai/chat-element/modules/helpers-text.js000064400000002332151716470030016647 0ustar00/* global WPFormsAIChatHTMLElement */ /** * The WPForms AI chat element. * * Choices helpers module. * * @since 1.9.1 * * @param {WPFormsAIChatHTMLElement} chat The chat element. * * @return {Function} The app cloning function. */ export default function( chat ) { // eslint-disable-line no-unused-vars /** * The default `text` mode helpers object. * * @since 1.9.1 */ return { /** * Get the `text` answer based on AI response data. * * @since 1.9.1 * * @param {Object} response The AI response data. * * @return {string} HTML markup. */ getAnswer( response ) { return `

    ${ response?.heading ?? '' }

    ${ response?.text ?? '' }

    ${ response?.footer ?? '' } `; }, /** * Get the answer pre-buttons HTML markup. * * @since 1.9.1 * * @return {string} The answer pre-buttons HTML markup. */ getAnswerButtonsPre() { return ''; }, /** * Added answer callback. * * @since 1.9.1 */ addedAnswer() {}, /** * Determine whether the Welcome Screen should be displayed. * * @since 1.9.2 * * @return {boolean} Display the Welcome Screen or not. */ isWelcomeScreen() { return true; }, }; } integrations/ai/chat-element/modules/helpers-text.min.js000064400000000345151716470030017433 0ustar00export default function(e){return{getAnswer(e){return`

    ${e?.heading??""}

    ${e?.text??""}

    ${e?.footer??""} `},getAnswerButtonsPre(){return""},addedAnswer(){},isWelcomeScreen(){return!0}}}integrations/ai/chat-element/wpforms-ai-chat-element.js000064400000073320151716470030017210 0ustar00/* global wpforms_ai_chat_element, WPFormsAIModal, wpf */ /** * @param this.modeStrings.learnMore * @param wpforms_ai_chat_element.dislike * @param wpforms_ai_chat_element.refresh * @param wpforms_ai_chat_element.confirm.refreshTitle * @param wpforms_ai_chat_element.confirm.refreshMessage * @param this.modeStrings.samplePrompts * @param this.modeStrings.errors.rate_limit * @param this.modeStrings.reasons.rate_limit * @param this.modeStrings.descrEndDot */ /** * The `WPFormsAIChatHTMLElement` element loader. * * @since 1.9.2 */ ( function() { // Additional modules provided by wpforms_ai_chat_element. const modules = wpforms_ai_chat_element.modules || []; // Import all modules dynamically. Promise.all( modules.map( ( module ) => import( module.path ) ) ) .then( ( importedModules ) => { // Create the helpers object dynamically. const helpers = {}; let api; importedModules.forEach( ( module, index ) => { const moduleName = modules[ index ].name; if ( moduleName === 'api' ) { api = module.default(); return; } helpers[ moduleName ] = module.default; } ); window.WPFormsAi = { api, helpers, }; // Register the custom HTML element. customElements.define( 'wpforms-ai-chat', WPFormsAIChatHTMLElement ); // eslint-disable-line no-use-before-define } ) .catch( ( error ) => { wpf.debug( 'Error importing modules:', error ); } ); }() ); /** * The WPForms AI chat. * * Custom HTML element class. * * @since 1.9.1 */ class WPFormsAIChatHTMLElement extends HTMLElement { /** * Element constructor. * * @since 1.9.1 */ constructor() { // eslint-disable-line no-useless-constructor // Always call super first in constructor. super(); } /** * Element connected to the DOM. * * @since 1.9.1 */ connectedCallback() { // eslint-disable-line complexity // Init chat properties. this.chatMode = this.getAttribute( 'mode' ) ?? 'text'; this.fieldId = this.getAttribute( 'field-id' ) ?? ''; this.prefill = this.getAttribute( 'prefill' ) ?? ''; this.autoSubmit = this.getAttribute( 'auto-submit' ) === 'true'; this.modeStrings = wpforms_ai_chat_element[ this.chatMode ] ?? {}; this.loadingState = false; // Init chat helpers according to the chat mode. this.modeHelpers = this.getHelpers( this ); // Bail if chat mode helpers not found. if ( ! this.modeHelpers ) { console.error( `WPFormsAI error: chat mode "${ this.chatMode }" helpers not found` ); // eslint-disable-line no-console return; } // Render chat HTML. if ( ! this.innerHTML.trim() ) { this.innerHTML = this.getInnerHTML(); } // Get chat elements. this.wrapper = this.querySelector( '.wpforms-ai-chat' ); this.input = this.querySelector( '.wpforms-ai-chat-message-input input, .wpforms-ai-chat-message-input textarea' ); this.welcomeScreenSamplePrompts = this.querySelector( '.wpforms-ai-chat-welcome-screen-sample-prompts' ); this.sendButton = this.querySelector( '.wpforms-ai-chat-send' ); this.stopButton = this.querySelector( '.wpforms-ai-chat-stop' ); this.messageList = this.querySelector( '.wpforms-ai-chat-message-list' ); // Flags. this.isTextarea = this.input.tagName === 'TEXTAREA'; this.preventResizeInput = false; // Compact scrollbar for non-Mac devices. if ( ! navigator.userAgent.includes( 'Macintosh' ) ) { this.messageList.classList.add( 'wpforms-scrollbar-compact' ); } // Bind events. this.events(); // Init answers. this.initAnswers(); // Init mode. if ( typeof this.modeHelpers.init === 'function' ) { this.modeHelpers.init(); } // Auto-submit if enabled and prefill is provided if ( this.autoSubmit && this.prefill ) { this.input.value = this.prefill; setTimeout( () => this.sendMessage( true ), 250 ); } } /** * Get initial innerHTML markup. * * @since 1.9.1 * * @return {string} The inner HTML markup. */ getInnerHTML() { if ( this.modeStrings.chatHtml ) { return this.decodeHTMLEntities( this.modeStrings.chatHtml ); } return `
    ${ this.getWelcomeScreen() }
    ${ this.getMessageInputField() }
    `; } /** * Get the message input field HTML. * * @since 1.9.2 * * @return {string} The message input field markup. */ getMessageInputField() { if ( typeof this.modeHelpers.getMessageInputField === 'function' ) { return this.modeHelpers.getMessageInputField(); } return ``; } /** * Get the Welcome screen HTML markup. * * @since 1.9.1 * * @return {string} The Welcome screen markup. */ getWelcomeScreen() { let content; if ( this.modeHelpers.isWelcomeScreen() ) { content = this.getWelcomeScreenContent(); } else { this.messagePreAdded = true; content = this.modeHelpers.getWarningMessage(); } return `

    ${ this.modeStrings.title }

    ${ this.modeStrings.description } ${ this.modeStrings.learnMore }${ this.modeStrings.descrEndDot }
    ${ content }
    `; } /** * Get the welcome screen content. * * @since 1.9.4 * * @return {string} The welcome screen content. */ getWelcomeScreenContent() { const samplePrompts = this.modeStrings?.samplePrompts, li = []; if ( ! samplePrompts && ! this.modeStrings?.initialChat ) { return ''; } if ( samplePrompts ) { // Render sample prompts. for ( const i in samplePrompts ) { li.push( `
  • ${ samplePrompts[ i ].title }
  • ` ); } return `
      ${ li.join( '' ) }
    `; } if ( this.prefill.length > 0 ) { return ''; } this.messagePreAdded = true; return this.modeHelpers?.getInitialChat( this.modeStrings.initialChat ); } /** * Get the spinner SVG image. * * @since 1.9.1 * * @return {string} The spinner SVG markup. */ getSpinnerSvg() { return ``; } /** * Add event listeners. * * @since 1.9.1 */ events() { this.sendButton.addEventListener( 'click', this.sendMessage.bind( this ) ); this.stopButton.addEventListener( 'click', this.stopLoading.bind( this ) ); this.input.addEventListener( 'keyup', this.keyUp.bind( this ) ); this.bindWelcomeScreenEvents(); } /** * Bind welcome screen events. * * @since 1.9.1 */ bindWelcomeScreenEvents() { if ( this.welcomeScreenSamplePrompts === null ) { return; } // Click on the default item in the welcome screen. this.welcomeScreenSamplePrompts.querySelectorAll( 'li' ).forEach( ( li ) => { li.addEventListener( 'click', this.clickDefaultItem.bind( this ) ); li.addEventListener( 'keydown', ( e ) => { if ( e.code === 'Enter' ) { e.preventDefault(); this.clickDefaultItem( e ); } } ); } ); } /** * Init all answers. * * @since 1.9.2 */ initAnswers() { if ( ! this.modeStrings.chatHtml ) { return; } this.wpformsAiApi = this.getAiApi(); this.messageList.querySelectorAll( '.wpforms-chat-item-answer' ).forEach( ( answer ) => { this.initAnswer( answer ); } ); } /** * Keyboard `keyUp` event handler. * * @since 1.9.1 * * @param {KeyboardEvent} e The keyboard event. */ keyUp( e ) { // eslint-disable-line complexity switch ( e.code ) { case 'Enter': // Send a message on `Enter` key press. // In the case of textarea, `Shift + Enter` adds a new line. if ( ! this.isTextarea || ( this.isTextarea && ! e.shiftKey ) ) { e.preventDefault(); this.sendMessage(); } break; case 'ArrowUp': // Navigate through the chat history. // In the case of textarea, `Ctrl + Up` is used. if ( ! this.isTextarea || ( this.isTextarea && e.ctrlKey ) ) { e.preventDefault(); this.arrowUp(); } break; case 'ArrowDown': // Navigate through the chat history. // In the case of textarea, `Ctrl + Down` is used. if ( ! this.isTextarea || ( this.isTextarea && e.ctrlKey ) ) { e.preventDefault(); this.arrowDown(); } break; default: // Update the chat history. this.history.update( { question: this.input.value } ); } } /** * Send a question message to the chat. * * @since 1.9.1 * * @param {boolean} allowHTML Whether to allow HTML in the message. */ sendMessage( allowHTML = false ) { let message = this.input.value; if ( ! message ) { return; } if ( ! allowHTML ) { message = this.htmlSpecialChars( message ); } // Fire event before sending the message. this.triggerEvent( 'wpformsAIChatBeforeSendMessage', { fieldId: this.fieldId, mode: this.chatMode } ); this.addFirstMessagePre(); this.welcomeScreenSamplePrompts?.remove(); this.resetInput(); this.addMessage( message, true ); this.startLoading(); if ( message.trim() === '' ) { this.addEmptyResultsError(); return; } if ( typeof this.modeHelpers.prepareMessage === 'function' ) { message = this.modeHelpers.prepareMessage( message ); } this.getAiApi() .prompt( message, this.sessionId ) .then( this.addAnswer.bind( this ) ) .catch( this.apiResponseError.bind( this ) ); } /** * AI API error handler. * * @since 1.9.2 * * @param {Object|string} error The error object or string. */ apiResponseError( error ) { // eslint-disable-line complexity const cause = error?.cause ?? null; // Handle the rate limit error. if ( cause === 429 ) { this.addError( this.modeStrings.errors.rate_limit || wpforms_ai_chat_element.errors.rate_limit, this.modeStrings.reasons.rate_limit || wpforms_ai_chat_element.reasons.rate_limit ); return; } // Handle the Internal Server Error. if ( cause === 500 ) { this.addEmptyResultsError(); return; } this.addError( error.message || this.modeStrings.errors.default || wpforms_ai_chat_element.errors.default, this.modeStrings.reasons.default || wpforms_ai_chat_element.reasons.default ); wpf.debug( 'WPFormsAI error: ', error ); } /** * Before the first message. * * @since 1.9.1 */ addFirstMessagePre() { if ( this.sessionId || this.messagePreAdded ) { return; } this.messagePreAdded = true; const divider = document.createElement( 'div' ); divider.classList.add( 'wpforms-ai-chat-divider' ); this.messageList.appendChild( divider ); } /** * Click on the default item in the welcome screen. * * @since 1.9.1 * * @param {Event} e The event object. */ clickDefaultItem( e ) { const li = e.target.nodeName === 'LI' ? e.target : e.target.closest( 'li' ); const message = li.querySelector( 'a' )?.textContent; e.preventDefault(); if ( ! message ) { return; } this.input.value = message; // Update the chat history. this.history.push( { question: message } ); this.sendMessage(); } /** * Click on the dislike button. * * @since 1.9.1 * * @param {Event} e The event object. */ clickDislikeButton( e ) { const button = e.target; const answer = button?.closest( '.wpforms-chat-item-answer' ); if ( ! answer ) { return; } button.classList.add( 'clicked' ); button.setAttribute( 'disabled', true ); const responseId = answer.getAttribute( 'data-response-id' ); this.wpformsAiApi.rate( false, responseId ); } /** * Click on the refresh button. * * @since 1.9.1 */ async clickRefreshButton() { const refreshConfirm = () => { // Restore the welcome screen. this.prefill = ''; this.messageList.innerHTML = this.getWelcomeScreen(); this.welcomeScreenSamplePrompts = this.querySelector( '.wpforms-ai-chat-welcome-screen-sample-prompts' ); this.bindWelcomeScreenEvents(); this.scrollMessagesTo( 'top' ); // Clear the session ID. this.wpformsAiApi = null; this.sessionId = null; this.messagePreAdded = null; this.wrapper.removeAttribute( 'data-session-id' ); // Clear the chat history. this.history.clear(); // Fire the event after refreshing the chat. this.triggerEvent( 'wpformsAIChatAfterRefresh', { fieldId: this.fieldId } ); }; const refreshCancel = () => { // Fire the event when refresh is canceled. this.triggerEvent( 'wpformsAIChatCancelRefresh', { fieldId: this.fieldId } ); }; // Fire the event before refresh confirmation is opened. this.triggerEvent( 'wpformsAIChatBeforeRefreshConfirm', { fieldId: this.fieldId } ); // Open a confirmation modal. WPFormsAIModal.confirmModal( { title: wpforms_ai_chat_element.confirm.refreshTitle, content: wpforms_ai_chat_element.confirm.refreshMessage, onConfirm: refreshConfirm, onCancel: refreshCancel, } ); } /** * Start loading. * * @since 1.9.1 */ startLoading() { this.loadingState = true; this.sendButton.classList.add( 'wpforms-hidden' ); this.stopButton.classList.remove( 'wpforms-hidden' ); this.input.setAttribute( 'disabled', true ); this.input.setAttribute( 'placeholder', this.modeStrings.waiting ); } /** * Stop loading. * * @since 1.9.1 */ stopLoading() { this.loadingState = false; this.messageList.querySelector( '.wpforms-chat-item-answer-waiting' )?.remove(); this.sendButton.classList.remove( 'wpforms-hidden' ); this.stopButton.classList.add( 'wpforms-hidden' ); this.input.removeAttribute( 'disabled' ); this.input.setAttribute( 'placeholder', this.modeStrings.placeholder ); this.input.focus(); } /** * Keyboard `ArrowUp` key event handler. * * @since 1.9.1 */ arrowUp() { const prev = this.history.prev()?.question; if ( typeof prev !== 'undefined' ) { this.input.value = prev; } } /** * Keyboard `ArrowDown` key event handler. * * @since 1.9.1 */ arrowDown() { const next = this.history.next()?.question; if ( typeof next !== 'undefined' ) { this.input.value = next; } } /** * Get AI API object instance. * * @since 1.9.1 * * @return {Object} The AI API object. */ getAiApi() { if ( this.wpformsAiApi ) { return this.wpformsAiApi; } // Attempt to get the session ID from the element attribute OR the data attribute. // It is necessary to restore the session ID after restoring the chat element. this.sessionId = this.wrapper.getAttribute( 'data-session-id' ) || null; // Create a new AI API object instance. this.wpformsAiApi = window.WPFormsAi.api( this.chatMode, this.sessionId ); return this.wpformsAiApi; } /** * Scroll message list to given edge. * * @since 1.9.1 * * @param {string} edge The edge to scroll to; `top` or `bottom`. */ scrollMessagesTo( edge = 'bottom' ) { if ( edge === 'top' ) { this.messageList.scrollTop = 0; return; } if ( this.messageList.scrollHeight - this.messageList.scrollTop < 22 ) { return; } this.messageList.scrollTop = this.messageList.scrollHeight; } /** * Add a message to the chat. * * @since 1.9.1 * * @param {string} message The message to add. * @param {boolean} isQuestion Whether it is a question. * @param {Object} response The response data, optional. * * @return {HTMLElement} The message element. */ addMessage( message, isQuestion, response = null ) { const { messageList } = this; const element = document.createElement( 'div' ); element.classList.add( 'wpforms-chat-item' ); messageList.appendChild( element ); if ( isQuestion ) { // Add a question. element.innerHTML = message; element.classList.add( 'wpforms-chat-item-question' ); // Add a waiting spinner. const spinnerWrapper = document.createElement( 'div' ), spinner = document.createElement( 'div' ); spinnerWrapper.classList.add( 'wpforms-chat-item-answer-waiting' ); spinner.classList.add( 'wpforms-chat-item-spinner' ); spinner.innerHTML = this.getSpinnerSvg(); spinnerWrapper.appendChild( spinner ); messageList.appendChild( spinnerWrapper ); // Add an empty chat history item. this.history.push( {} ); } else { // Add an answer. const itemContent = document.createElement( 'div' ); itemContent.classList.add( 'wpforms-chat-item-content' ); element.appendChild( itemContent ); // Remove the waiting spinner. messageList.querySelector( '.wpforms-chat-item-answer-waiting' )?.remove(); // Remove the active class from the previous answer. this.messageList.querySelector( '.wpforms-chat-item-answer.active' )?.classList.remove( 'active' ); // Update element classes and attributes. element.classList.add( 'wpforms-chat-item-answer' ); element.classList.add( 'active' ); element.classList.add( 'wpforms-chat-item-typing' ); element.classList.add( 'wpforms-chat-item-' + this.chatMode ); element.setAttribute( 'data-response-id', response?.responseId ?? '' ); // Update the answer in the chat history. this.history.update( { answer: message } ); // Type the message with the typewriter effect. this.typeText( itemContent, message, this.addedAnswer.bind( this ) ); } this.scrollMessagesTo( 'bottom' ); return element; } /** * Add an error to the chat. * * @since 1.9.1 * * @param {string} errorTitle The error title. * @param {string} errorReason The error title. */ addError( errorTitle, errorReason ) { this.addNotice( errorTitle, errorReason ); } /** * Add a warning to the chat. * * @since 1.9.2 * * @param {string} warningTitle The warning title. * @param {string} warningReason The warning reason. */ addWarning( warningTitle, warningReason ) { this.addNotice( warningTitle, warningReason, 'warning' ); } /** * Add a notice to the chat. * * @since 1.9.2 * * @param {string} title The notice title. * @param {string} reason The notice reason. * @param {string} type The notice type. */ addNotice( title, reason, type = 'error' ) { let content = ``; // Bail if loading was stopped. if ( ! this.loadingState ) { return; } if ( title ) { content += `

    ${ title }

    `; } if ( reason ) { content += `${ reason }`; } const chatItem = document.createElement( 'div' ); const itemContent = document.createElement( 'div' ); chatItem.classList.add( 'wpforms-chat-item' ); chatItem.classList.add( 'wpforms-chat-item-' + type ); itemContent.classList.add( 'wpforms-chat-item-content' ); chatItem.appendChild( itemContent ); this.messageList.querySelector( '.wpforms-chat-item-answer-waiting' )?.remove(); this.messageList.appendChild( chatItem ); // Add the error to the chat. // Type the message with the typewriter effect. this.typeText( itemContent, content, () => { this.stopLoading(); } ); } /** * Add an empty results error to the chat. * * @since 1.9.1 */ addEmptyResultsError() { this.addError( this.modeStrings.errors.empty || wpforms_ai_chat_element.errors.empty, this.modeStrings.reasons.empty || wpforms_ai_chat_element.reasons.empty ); } /** * Add a prohibited code warning to the chat. * * @since 1.9.2 */ addProhibitedCodeWarning() { this.addWarning( this.modeStrings.warnings.prohibited_code || wpforms_ai_chat_element.warnings.prohibited_code, this.modeStrings.reasons.prohibited_code || wpforms_ai_chat_element.reasons.prohibited_code ); } /** * Add an answer to the chat. * * @since 1.9.1 * * @param {Object} response The response data to add. */ addAnswer( response ) { // Bail if loading was stopped. if ( ! this.loadingState || ! response ) { return; } // Output processing time to console if available. if ( response.processingData ) { wpf.debug( 'WPFormsAI processing data:', response.processingData ); } // Sanitize response. const sanitizedResponse = this.sanitizeResponse( { ...response } ); if ( this.hasProhibitedCode( response, sanitizedResponse ) ) { this.addProhibitedCodeWarning(); return; } const answerHTML = this.modeHelpers.getAnswer( sanitizedResponse ); if ( ! answerHTML ) { this.addEmptyResultsError(); return; } // Store the session ID from response. this.sessionId = response.sessionId; // Set the session ID to the chat wrapper data attribute. this.wrapper.setAttribute( 'data-session-id', this.sessionId ); // Fire the event before adding the answer to the chat. this.triggerEvent( 'wpformsAIChatBeforeAddAnswer', { chat: this, response: sanitizedResponse } ); // Add the answer to the chat. this.addMessage( answerHTML, false, sanitizedResponse ); this.triggerEvent( 'wpformsAIChatAfterAddAnswer', { fieldId: this.fieldId } ); } /** * Check if the response has a prohibited code. * * @since 1.9.2 * * @param {Object} response The response data. * @param {Array} sanitizedResponse The sanitized response data. * * @return {boolean} Whether the answer has a prohibited code. */ hasProhibitedCode( response, sanitizedResponse ) { if ( typeof this.modeHelpers.hasProhibitedCode === 'function' ) { return this.modeHelpers.hasProhibitedCode( response, sanitizedResponse ); } return false; } /** * Sanitize response. * * @since 1.9.2 * * @param {Object} response The response data to sanitize. * * @return {Object} The sanitized response. */ sanitizeResponse( response ) { if ( typeof this.modeHelpers.sanitizeResponse === 'function' ) { return this.modeHelpers.sanitizeResponse( response ); } return response; } /** * The added answer callback. * * @since 1.9.1 * * @param {HTMLElement} element The answer element. */ addedAnswer( element ) { // Add answer buttons when typing is finished. element.innerHTML += this.getAnswerButtons(); element.parentElement.classList.remove( 'wpforms-chat-item-typing' ); this.stopLoading(); this.initAnswer( element ); // Added answer callback. this.modeHelpers.addedAnswer( element ); // Fire the event when the answer added to the chat. this.triggerEvent( 'wpformsAIChatAddedAnswer', { chat: this, element } ); } /** * Init answer. * * @since 1.9.2 * * @param {HTMLElement} element The answer element. */ initAnswer( element ) { if ( ! element ) { return; } // Prepare answer buttons and init the tooltips. element.querySelectorAll( '.wpforms-help-tooltip' ).forEach( ( icon ) => { let title = icon.getAttribute( 'title' ); if ( ! title ) { title = icon.classList.contains( 'dislike' ) ? wpforms_ai_chat_element.dislike : ''; title = icon.classList.contains( 'refresh' ) ? wpforms_ai_chat_element.refresh : title; icon.setAttribute( 'title', title ); } icon.classList.remove( 'tooltipstered' ); } ); wpf.initTooltips( element ); // Add event listeners. element.addEventListener( 'click', this.setActiveAnswer.bind( this ) ); element.querySelector( '.wpforms-ai-chat-answer-button.dislike' ) ?.addEventListener( 'click', this.clickDislikeButton.bind( this ) ); element.querySelector( '.wpforms-ai-chat-answer-button.refresh' ) ?.addEventListener( 'click', this.clickRefreshButton.bind( this ) ); } /** * Set active answer. * * @since 1.9.2 * * @param {Event} e The event object. */ setActiveAnswer( e ) { let answer = e.target.closest( '.wpforms-chat-item-answer' ); answer = answer || e.target; if ( answer.classList.contains( 'active' ) ) { return; } this.messageList.querySelector( '.wpforms-chat-item-answer.active' )?.classList.remove( 'active' ); answer.classList.add( 'active' ); const responseId = answer.getAttribute( 'data-response-id' ); if ( this.modeHelpers.setActiveAnswer ) { this.modeHelpers.setActiveAnswer( answer ); } // Trigger the event. this.triggerEvent( 'wpformsAIChatSetActiveAnswer', { chat: this, responseId } ); } /** * Get the answer buttons HTML markup. * * @since 1.9.1 * * @return {string} The answer buttons HTML markup. */ getAnswerButtons() { return `
    ${ this.modeHelpers.getAnswerButtonsPre() }
    `; } /** * Type text into an element with the typewriter effect. * * @since 1.9.1 * * @param {HTMLElement} element The element to type into. * @param {string} text The text to type. * @param {Function} finishedCallback The callback function to call when typing is finished. */ typeText( element, text, finishedCallback ) { const chunkSize = 5; const chat = this; let index = 0; let content = ''; /** * Type single character. * * @since 1.9.1 */ function type() { const chunk = text.substring( index, index + chunkSize ); content += chunk; // Remove broken HTML tag from the end of the string. element.innerHTML = content.replace( /<[^>]{0,300}$/g, '' ); index += chunkSize; if ( index < text.length && chat.loadingState ) { // Recursive call to output the next chunk. setTimeout( type, 20 ); } else if ( typeof finishedCallback === 'function' ) { // Call the callback function when typing is finished. finishedCallback( element ); } chat.scrollMessagesTo( 'bottom' ); } type(); } /** * Get the `helpers` object according to the chat mode. * * @since 1.9.1 * * @param {WPFormsAIChatHTMLElement} chat Chat element. * * @return {Object} Choices helpers object. */ getHelpers( chat ) { const helpers = window.WPFormsAi.helpers; return helpers[ chat.chatMode ]( chat ) ?? null; } /** * Reset the message input field. * * @since 1.9.2 */ resetInput() { this.input.value = ''; if ( this.modeHelpers.resetInput ) { this.modeHelpers.resetInput(); } } /** * Escape HTML special characters. * * @since 1.9.1 * * @param {string} html HTML string. * * @return {string} Escaped HTML string. */ htmlSpecialChars( html ) { return html.replace( /[<>]/g, ( x ) => '�' + x.charCodeAt( 0 ) + ';' ); } /** * Decode HTML entities. * * @since 1.9.2 * * @param {string} html Encoded HTML string. * * @return {string} Decoded HTML string. */ decodeHTMLEntities( html ) { const txt = document.createElement( 'textarea' ); txt.innerHTML = html; return txt.value; } /** * Wrapper to trigger a custom event and return the event object. * * @since 1.9.1 * * @param {string} eventName Event name to trigger (custom or native). * @param {Object} args Trigger arguments. * * @return {Event} Event object. */ triggerEvent( eventName, args = {} ) { const event = new CustomEvent( eventName, { detail: args } ); document.dispatchEvent( event ); return event; } /** * Chat history object. * * @since 1.9.1 */ history = { /** * Chat history data. * * @since 1.9.1 * * @type {Array} */ data: [], /** * Chat history pointer. * * @since 1.9.1 * * @type {number} */ pointer: 0, /** * Default item. * * @since 1.9.1 * * @type {Object} */ defaultItem: { question: '', answer: null, }, /** * Get history data by pointer. * * @since 1.9.1 * * @param {number|null} pointer The history pointer. * * @return {Object} The history item. */ get( pointer = null ) { if ( pointer ) { this.pointer = pointer; } if ( this.pointer < 1 ) { this.pointer = 0; } else if ( this.pointer >= this.data.length ) { this.pointer = this.data.length - 1; } return this.data[ this.pointer ] ?? {}; }, /** * Get history data by pointer. * * @since 1.9.1 * * @return {Object} The history item. */ prev() { this.pointer -= 1; return this.get(); }, /** * Get history data by pointer. * * @since 1.9.1 * * @return {Object} The history item. */ next() { this.pointer += 1; return this.get(); }, /** * Push an item to the chat history. * * @since 1.9.1 * * @param {Object} item The item to push. * * @return {void} */ push( item ) { if ( item.answer ) { this.data[ this.data.length - 1 ].answer = item.answer; return; } this.data.push( { ...this.defaultItem, ...item } ); this.pointer = this.data.length - 1; }, /** * Update the last history item. * * @since 1.9.1 * * @param {Object} item The updated history item. * * @return {void} */ update( item ) { const lastKey = this.data.length > 0 ? this.data.length - 1 : 0; const lastItem = this.data[ lastKey ] ?? this.defaultItem; this.pointer = lastKey; this.data[ lastKey ] = { ...lastItem, ...item }; }, /** * Clear the chat history. * * @since 1.9.1 */ clear() { this.data = []; this.pointer = 0; }, }; } integrations/ai/chat-element/wpforms-ai-chat-element.min.js000064400000034613151716470030017774 0ustar00!function(){const r=wpforms_ai_chat_element.modules||[];Promise.all(r.map(e=>import(e.path))).then(e=>{const s={};let i;e.forEach((e,t)=>{t=r[t].name;"api"===t?i=e.default():s[t]=e.default}),window.WPFormsAi={api:i,helpers:s},customElements.define("wpforms-ai-chat",WPFormsAIChatHTMLElement)}).catch(e=>{wpf.debug("Error importing modules:",e)})}();class WPFormsAIChatHTMLElement extends HTMLElement{constructor(){super()}connectedCallback(){this.chatMode=this.getAttribute("mode")??"text",this.fieldId=this.getAttribute("field-id")??"",this.prefill=this.getAttribute("prefill")??"",this.autoSubmit="true"===this.getAttribute("auto-submit"),this.modeStrings=wpforms_ai_chat_element[this.chatMode]??{},this.loadingState=!1,this.modeHelpers=this.getHelpers(this),this.modeHelpers?(this.innerHTML.trim()||(this.innerHTML=this.getInnerHTML()),this.wrapper=this.querySelector(".wpforms-ai-chat"),this.input=this.querySelector(".wpforms-ai-chat-message-input input, .wpforms-ai-chat-message-input textarea"),this.welcomeScreenSamplePrompts=this.querySelector(".wpforms-ai-chat-welcome-screen-sample-prompts"),this.sendButton=this.querySelector(".wpforms-ai-chat-send"),this.stopButton=this.querySelector(".wpforms-ai-chat-stop"),this.messageList=this.querySelector(".wpforms-ai-chat-message-list"),this.isTextarea="TEXTAREA"===this.input.tagName,this.preventResizeInput=!1,navigator.userAgent.includes("Macintosh")||this.messageList.classList.add("wpforms-scrollbar-compact"),this.events(),this.initAnswers(),"function"==typeof this.modeHelpers.init&&this.modeHelpers.init(),this.autoSubmit&&this.prefill&&(this.input.value=this.prefill,setTimeout(()=>this.sendMessage(!0),250))):console.error(`WPFormsAI error: chat mode "${this.chatMode}" helpers not found`)}getInnerHTML(){return this.modeStrings.chatHtml?this.decodeHTMLEntities(this.modeStrings.chatHtml):`
    ${this.getWelcomeScreen()}
    ${this.getMessageInputField()}
    `}getMessageInputField(){return"function"==typeof this.modeHelpers.getMessageInputField?this.modeHelpers.getMessageInputField():``}getWelcomeScreen(){let e;return e=this.modeHelpers.isWelcomeScreen()?this.getWelcomeScreenContent():(this.messagePreAdded=!0,this.modeHelpers.getWarningMessage()),`

    ${this.modeStrings.title}

    ${this.modeStrings.description} ${this.modeStrings.learnMore}${this.modeStrings.descrEndDot}
    ${e}
    `}getWelcomeScreenContent(){var e=this.modeStrings?.samplePrompts,t=[];if(!e&&!this.modeStrings?.initialChat)return"";if(e){for(const s in e)t.push(`
  • ${e[s].title}
  • `);return`
      ${t.join("")}
    `}return 0'}events(){this.sendButton.addEventListener("click",this.sendMessage.bind(this)),this.stopButton.addEventListener("click",this.stopLoading.bind(this)),this.input.addEventListener("keyup",this.keyUp.bind(this)),this.bindWelcomeScreenEvents()}bindWelcomeScreenEvents(){null!==this.welcomeScreenSamplePrompts&&this.welcomeScreenSamplePrompts.querySelectorAll("li").forEach(e=>{e.addEventListener("click",this.clickDefaultItem.bind(this)),e.addEventListener("keydown",e=>{"Enter"===e.code&&(e.preventDefault(),this.clickDefaultItem(e))})})}initAnswers(){this.modeStrings.chatHtml&&(this.wpformsAiApi=this.getAiApi(),this.messageList.querySelectorAll(".wpforms-chat-item-answer").forEach(e=>{this.initAnswer(e)}))}keyUp(e){switch(e.code){case"Enter":this.isTextarea&&(!this.isTextarea||e.shiftKey)||(e.preventDefault(),this.sendMessage());break;case"ArrowUp":(!this.isTextarea||this.isTextarea&&e.ctrlKey)&&(e.preventDefault(),this.arrowUp());break;case"ArrowDown":(!this.isTextarea||this.isTextarea&&e.ctrlKey)&&(e.preventDefault(),this.arrowDown());break;default:this.history.update({question:this.input.value})}}sendMessage(e=!1){let t=this.input.value;t&&(e||(t=this.htmlSpecialChars(t)),this.triggerEvent("wpformsAIChatBeforeSendMessage",{fieldId:this.fieldId,mode:this.chatMode}),this.addFirstMessagePre(),this.welcomeScreenSamplePrompts?.remove(),this.resetInput(),this.addMessage(t,!0),this.startLoading(),""===t.trim()?this.addEmptyResultsError():("function"==typeof this.modeHelpers.prepareMessage&&(t=this.modeHelpers.prepareMessage(t)),this.getAiApi().prompt(t,this.sessionId).then(this.addAnswer.bind(this)).catch(this.apiResponseError.bind(this))))}apiResponseError(e){var t=e?.cause??null;429===t?this.addError(this.modeStrings.errors.rate_limit||wpforms_ai_chat_element.errors.rate_limit,this.modeStrings.reasons.rate_limit||wpforms_ai_chat_element.reasons.rate_limit):500===t?this.addEmptyResultsError():(this.addError(e.message||this.modeStrings.errors.default||wpforms_ai_chat_element.errors.default,this.modeStrings.reasons.default||wpforms_ai_chat_element.reasons.default),wpf.debug("WPFormsAI error: ",e))}addFirstMessagePre(){var e;this.sessionId||this.messagePreAdded||(this.messagePreAdded=!0,(e=document.createElement("div")).classList.add("wpforms-ai-chat-divider"),this.messageList.appendChild(e))}clickDefaultItem(e){var t=("LI"===e.target.nodeName?e.target:e.target.closest("li")).querySelector("a")?.textContent;e.preventDefault(),t&&(this.input.value=t,this.history.push({question:t}),this.sendMessage())}clickDislikeButton(e){var e=e.target,t=e?.closest(".wpforms-chat-item-answer");t&&(e.classList.add("clicked"),e.setAttribute("disabled",!0),e=t.getAttribute("data-response-id"),this.wpformsAiApi.rate(!1,e))}async clickRefreshButton(){this.triggerEvent("wpformsAIChatBeforeRefreshConfirm",{fieldId:this.fieldId}),WPFormsAIModal.confirmModal({title:wpforms_ai_chat_element.confirm.refreshTitle,content:wpforms_ai_chat_element.confirm.refreshMessage,onConfirm:()=>{this.prefill="",this.messageList.innerHTML=this.getWelcomeScreen(),this.welcomeScreenSamplePrompts=this.querySelector(".wpforms-ai-chat-welcome-screen-sample-prompts"),this.bindWelcomeScreenEvents(),this.scrollMessagesTo("top"),this.wpformsAiApi=null,this.sessionId=null,this.messagePreAdded=null,this.wrapper.removeAttribute("data-session-id"),this.history.clear(),this.triggerEvent("wpformsAIChatAfterRefresh",{fieldId:this.fieldId})},onCancel:()=>{this.triggerEvent("wpformsAIChatCancelRefresh",{fieldId:this.fieldId})}})}startLoading(){this.loadingState=!0,this.sendButton.classList.add("wpforms-hidden"),this.stopButton.classList.remove("wpforms-hidden"),this.input.setAttribute("disabled",!0),this.input.setAttribute("placeholder",this.modeStrings.waiting)}stopLoading(){this.loadingState=!1,this.messageList.querySelector(".wpforms-chat-item-answer-waiting")?.remove(),this.sendButton.classList.remove("wpforms-hidden"),this.stopButton.classList.add("wpforms-hidden"),this.input.removeAttribute("disabled"),this.input.setAttribute("placeholder",this.modeStrings.placeholder),this.input.focus()}arrowUp(){var e=this.history.prev()?.question;void 0!==e&&(this.input.value=e)}arrowDown(){var e=this.history.next()?.question;void 0!==e&&(this.input.value=e)}getAiApi(){return this.wpformsAiApi||(this.sessionId=this.wrapper.getAttribute("data-session-id")||null,this.wpformsAiApi=window.WPFormsAi.api(this.chatMode,this.sessionId)),this.wpformsAiApi}scrollMessagesTo(e="bottom"){"top"===e?this.messageList.scrollTop=0:this.messageList.scrollHeight-this.messageList.scrollTop<22||(this.messageList.scrollTop=this.messageList.scrollHeight)}addMessage(e,t,s=null){var i,r=this["messageList"],a=document.createElement("div");return a.classList.add("wpforms-chat-item"),r.appendChild(a),t?(a.innerHTML=e,a.classList.add("wpforms-chat-item-question"),t=document.createElement("div"),i=document.createElement("div"),t.classList.add("wpforms-chat-item-answer-waiting"),i.classList.add("wpforms-chat-item-spinner"),i.innerHTML=this.getSpinnerSvg(),t.appendChild(i),r.appendChild(t),this.history.push({})):((i=document.createElement("div")).classList.add("wpforms-chat-item-content"),a.appendChild(i),r.querySelector(".wpforms-chat-item-answer-waiting")?.remove(),this.messageList.querySelector(".wpforms-chat-item-answer.active")?.classList.remove("active"),a.classList.add("wpforms-chat-item-answer"),a.classList.add("active"),a.classList.add("wpforms-chat-item-typing"),a.classList.add("wpforms-chat-item-"+this.chatMode),a.setAttribute("data-response-id",s?.responseId??""),this.history.update({answer:e}),this.typeText(i,e,this.addedAnswer.bind(this))),this.scrollMessagesTo("bottom"),a}addError(e,t){this.addNotice(e,t)}addWarning(e,t){this.addNotice(e,t,"warning")}addNotice(e,t,s="error"){let i="";this.loadingState&&(e&&(i+=`

    ${e}

    `),t&&(i+=`${t}`),e=document.createElement("div"),t=document.createElement("div"),e.classList.add("wpforms-chat-item"),e.classList.add("wpforms-chat-item-"+s),t.classList.add("wpforms-chat-item-content"),e.appendChild(t),this.messageList.querySelector(".wpforms-chat-item-answer-waiting")?.remove(),this.messageList.appendChild(e),this.typeText(t,i,()=>{this.stopLoading()}))}addEmptyResultsError(){this.addError(this.modeStrings.errors.empty||wpforms_ai_chat_element.errors.empty,this.modeStrings.reasons.empty||wpforms_ai_chat_element.reasons.empty)}addProhibitedCodeWarning(){this.addWarning(this.modeStrings.warnings.prohibited_code||wpforms_ai_chat_element.warnings.prohibited_code,this.modeStrings.reasons.prohibited_code||wpforms_ai_chat_element.reasons.prohibited_code)}addAnswer(e){var t,s;this.loadingState&&e&&(e.processingData&&wpf.debug("WPFormsAI processing data:",e.processingData),t=this.sanitizeResponse({...e}),this.hasProhibitedCode(e,t)?this.addProhibitedCodeWarning():(s=this.modeHelpers.getAnswer(t))?(this.sessionId=e.sessionId,this.wrapper.setAttribute("data-session-id",this.sessionId),this.triggerEvent("wpformsAIChatBeforeAddAnswer",{chat:this,response:t}),this.addMessage(s,!1,t),this.triggerEvent("wpformsAIChatAfterAddAnswer",{fieldId:this.fieldId})):this.addEmptyResultsError())}hasProhibitedCode(e,t){return"function"==typeof this.modeHelpers.hasProhibitedCode&&this.modeHelpers.hasProhibitedCode(e,t)}sanitizeResponse(e){return"function"==typeof this.modeHelpers.sanitizeResponse?this.modeHelpers.sanitizeResponse(e):e}addedAnswer(e){e.innerHTML+=this.getAnswerButtons(),e.parentElement.classList.remove("wpforms-chat-item-typing"),this.stopLoading(),this.initAnswer(e),this.modeHelpers.addedAnswer(e),this.triggerEvent("wpformsAIChatAddedAnswer",{chat:this,element:e})}initAnswer(e){e&&(e.querySelectorAll(".wpforms-help-tooltip").forEach(e=>{var t;e.getAttribute("title")||(t=e.classList.contains("dislike")?wpforms_ai_chat_element.dislike:"",t=e.classList.contains("refresh")?wpforms_ai_chat_element.refresh:t,e.setAttribute("title",t)),e.classList.remove("tooltipstered")}),wpf.initTooltips(e),e.addEventListener("click",this.setActiveAnswer.bind(this)),e.querySelector(".wpforms-ai-chat-answer-button.dislike")?.addEventListener("click",this.clickDislikeButton.bind(this)),e.querySelector(".wpforms-ai-chat-answer-button.refresh")?.addEventListener("click",this.clickRefreshButton.bind(this)))}setActiveAnswer(e){var t=(t=e.target.closest(".wpforms-chat-item-answer"))||e.target;t.classList.contains("active")||(this.messageList.querySelector(".wpforms-chat-item-answer.active")?.classList.remove("active"),t.classList.add("active"),e=t.getAttribute("data-response-id"),this.modeHelpers.setActiveAnswer&&this.modeHelpers.setActiveAnswer(t),this.triggerEvent("wpformsAIChatSetActiveAnswer",{chat:this,responseId:e}))}getAnswerButtons(){return`
    ${this.modeHelpers.getAnswerButtonsPre()}
    `}typeText(s,i,r){const a=this;let n=0,o="";!function e(){var t=i.substring(n,n+5);o+=t,s.innerHTML=o.replace(/<[^>]{0,300}$/g,""),(n+=5)]/g,e=>"�"+e.charCodeAt(0)+";")}decodeHTMLEntities(e){var t=document.createElement("textarea");return t.innerHTML=e,t.value}triggerEvent(e,t={}){e=new CustomEvent(e,{detail:t});return document.dispatchEvent(e),e}history={data:[],pointer:0,defaultItem:{question:"",answer:null},get(e=null){return e&&(this.pointer=e),this.pointer<1?this.pointer=0:this.pointer>=this.data.length&&(this.pointer=this.data.length-1),this.data[this.pointer]??{}},prev(){return--this.pointer,this.get()},next(){return this.pointer+=1,this.get()},push(e){e.answer?this.data[this.data.length-1].answer=e.answer:(this.data.push({...this.defaultItem,...e}),this.pointer=this.data.length-1)},update(e){var t=0`; args.theme = `wpforms-ai-modal, wpforms-ai-purple, wpforms-ai-modal-choices-${ fieldId }`; args.backgroundDismiss = hideChoices; args.backgroundDismissAnimation = ''; args.contentMaxHeight = Math.min( app.defaultOptions.contentMaxHeight, app.getMaxModalHeight() ); args.onOpen = function() { // Unbind the click event from the close icon and use our own instead. this.$closeIcon.off( 'click' ); this.$closeIcon.on( 'click', hideChoices ); }; app.initModal( args ); }, /** * Hide the choices modal window. * * @since 1.9.1 * * @param {string} fieldId Choice field ID. */ hideChoicesModal( fieldId ) { $( `.jconfirm-wpforms-ai-modal-choices-${ fieldId }` ).addClass( 'wpforms-hidden' ).fadeOut(); }, /** * Show the choices modal window. * * @since 1.9.1 * * @param {string} fieldId Choice field ID. */ showChoicesModal( fieldId ) { $( `.jconfirm-wpforms-ai-modal-choices-${ fieldId }` ).removeClass( 'wpforms-hidden' ).fadeIn(); }, /** * Resize choices modal window height. * * @since 1.9.4 * * @param {string} fieldId Field ID. */ resizeModalHeight( fieldId ) { const modalHeight = app.getMaxModalHeight(); const $modal = $( '.jconfirm-wpforms-ai-modal' ).filter( function() { // find class starts with jconfirm-wpforms-ai-modal- and ends with -{fieldId}. return $( this ).attr( 'class' ).match( new RegExp( 'jconfirm-wpforms-ai-modal-.*-' + fieldId, 'i' ) ); } ); $modal.find( '.jconfirm-content-pane' ) .css( { height: modalHeight, 'max-height': modalHeight, } ); }, /** * Before choices refresh confirm is displayed. * * @since 1.9.1 * * @param {Event} e Event object. */ beforeChoicesRefreshConfirm( e ) { const fieldId = e.detail?.fieldId || 0; app.hideChoicesModal( fieldId ); }, /** * Cancel choices' refresh. * * @since 1.9.1 * * @param {Event} e Event object. */ cancelChoicesRefresh( e ) { const fieldId = e.detail?.fieldId || 0; app.showChoicesModal( fieldId ); }, /** * Refresh the main modal window height. * * @since 1.9.1 * * @param {Event} e Event object. */ refreshModalHeight( e ) { const fieldId = e.detail?.fieldId || 0; const maxHeight = Math.min( app.getMaxModalHeight(), app.defaultOptions.contentMaxHeight ); app.showChoicesModal( fieldId ); // Reset choices modal window height. $( `.jconfirm-wpforms-ai-modal-choices-${ fieldId } .jconfirm-content-pane` ) .css( { height: maxHeight, 'max-height': maxHeight, } ); }, /** * Get the max modal height. * * @since 1.9.1 * * @return {number} The max modal height. */ getMaxModalHeight() { // 80% of the window height, but not more than 800 px. return Math.min( $( window ).height() * 0.8, 800 ); }, /** * Extend jquery-confirm plugin with support of max-height for the content area. * * @since 1.9.1 */ extendJqueryConfirm() { // Extend a method of global instance. window.Jconfirm.prototype._updateContentMaxHeight = function() { this.$contentPane.css( { 'max-height': this.contentMaxHeight + 'px', } ); }; }, /** * Confirm a modal window. * * This is a wrapper for the `jquery.confirm` plugin. * * @since 1.9.1 * * @param {Object} args Modal window arguments. */ confirmModal( args ) { const options = { title: false, content: '', icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_ai_chat_element.btnYes, btnClass: 'btn-confirm', keys: [ 'enter' ], action() { if ( typeof args.onConfirm === 'function' ) { args.onConfirm(); } }, }, cancel: { text: wpforms_ai_chat_element.btnCancel, action() { if ( typeof args.onCancel === 'function' ) { args.onCancel(); } }, }, }, }; $.confirm( { ...options, ...args } ); }, }; // Provide access to public functions/properties. return app; }( document, window, jQuery ) ); // Initialize. WPFormsAIModal.init(); integrations/ai/choices/wpforms-ai-modal.min.js000064400000006170151716470030015566 0ustar00var WPFormsAIModal=window.WPFormsAIModal||function(e,o,a){const s={defaultOptions:{title:!1,content:"",type:"ai",smoothContent:!0,bgOpacity:1,boxWidth:650,contentMaxHeight:600,closeIcon:!0,buttons:!1},init(){a(s.ready)},ready(){s.extendJqueryConfirm(),s.bindChoicesActions()},bindChoicesActions(){a(e).on("click",".wpforms-ai-choices-button",s.initChoicesModal).on("wpformsAIChatBeforeRefreshConfirm",s.beforeChoicesRefreshConfirm).on("wpformsAIModalBeforeWarningMessageInsert",s.refreshModalHeight).on("wpformsAIChatAfterRefresh",s.refreshModalHeight).on("wpformsAIChatCancelRefresh",s.cancelChoicesRefresh).on("wpformsAIChatBeforeSendMessage",function(e){s.resizeModalHeight(e.detail.fieldId)}).on("wpformsAIChatAfterAddAnswer",function(e){s.resizeModalHeight(e.detail.fieldId)}).on("wpformsAIModalAfterChoicesInsert",function(e){s.hideChoicesModal(e.detail.fieldId)}),a(o).on("resize",function(){a(".jconfirm-wpforms-ai-modal wpforms-ai-chat").each(function(){s.resizeModalHeight(a(this).attr("field-id"))})})},initModal(e){a.confirm({...s.defaultOptions,...e})},initChoicesModal(){var e=a(this);if(e.hasClass("wpforms-prevent-default"))e.trigger("blur");else{const o=e.data("field-id"),i=a(".jconfirm-wpforms-ai-modal-choices-"+o);if(i.length)i.removeClass("wpforms-hidden").fadeIn();else{const t={},n=function(){return s.hideChoicesModal(o),!1};t.content=``,t.theme="wpforms-ai-modal, wpforms-ai-purple, wpforms-ai-modal-choices-"+o,t.backgroundDismiss=n,t.backgroundDismissAnimation="",t.contentMaxHeight=Math.min(s.defaultOptions.contentMaxHeight,s.getMaxModalHeight()),t.onOpen=function(){this.$closeIcon.off("click"),this.$closeIcon.on("click",n)},s.initModal(t)}}},hideChoicesModal(e){a(".jconfirm-wpforms-ai-modal-choices-"+e).addClass("wpforms-hidden").fadeOut()},showChoicesModal(e){a(".jconfirm-wpforms-ai-modal-choices-"+e).removeClass("wpforms-hidden").fadeIn()},resizeModalHeight(e){var o=s.getMaxModalHeight();a(".jconfirm-wpforms-ai-modal").filter(function(){return a(this).attr("class").match(new RegExp("jconfirm-wpforms-ai-modal-.*-"+e,"i"))}).find(".jconfirm-content-pane").css({height:o,"max-height":o})},beforeChoicesRefreshConfirm(e){e=e.detail?.fieldId||0;s.hideChoicesModal(e)},cancelChoicesRefresh(e){e=e.detail?.fieldId||0;s.showChoicesModal(e)},refreshModalHeight(e){var e=e.detail?.fieldId||0,o=Math.min(s.getMaxModalHeight(),s.defaultOptions.contentMaxHeight);s.showChoicesModal(e),a(`.jconfirm-wpforms-ai-modal-choices-${e} .jconfirm-content-pane`).css({height:o,"max-height":o})},getMaxModalHeight(){return Math.min(.8*a(o).height(),800)},extendJqueryConfirm(){o.Jconfirm.prototype._updateContentMaxHeight=function(){this.$contentPane.css({"max-height":this.contentMaxHeight+"px"})}},confirmModal(e){var o={title:!1,content:"",icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_ai_chat_element.btnYes,btnClass:"btn-confirm",keys:["enter"],action(){"function"==typeof e.onConfirm&&e.onConfirm()}},cancel:{text:wpforms_ai_chat_element.btnCancel,action(){"function"==typeof e.onCancel&&e.onCancel()}}}};a.confirm({...o,...e})}};return s}(document,window,jQuery);WPFormsAIModal.init();integrations/ai/form-generator/form-generator.js000064400000013277151716470030016075 0ustar00/* global wpforms_ai_form_generator, wpf, wpforms_ai_chat_element, WPFormsBuilder */ /** * @param strings.dismissed.installAddons * @param strings.isLicenseActive * @param strings.modules.main * @param strings.templateCard.buttonTextInit * @param strings.templateCard.imageSrc * @param strings.liteConnectAllowed * @param strings.liteConnectEnabled * @param strings.liteConnectNotAllowed * @param window.WPFormsAIFormGenerator * @param wpforms_builder.is_ai_disabled */ // noinspection ES6ConvertVarToLetConst /** * WPForms AI Form Generator. * * @since 1.9.2 */ // eslint-disable-next-line no-var var WPFormsAIFormGenerator = window.WPFormsAIFormGenerator || ( function( document, window, $ ) { /** * Localized strings. * * @since 1.9.2 * * @type {Object} */ const strings = wpforms_ai_form_generator; /** * Public functions and properties. * * @since 1.9.2 * * @type {Object} */ const app = { /** * State data holder. * * @since 1.9.2 * * @type {Object} */ state: {}, /** * Main module. * * @since 1.9.2 * * @type {Object} */ main: null, /** * The form preview module. * * @since 1.9.2 * * @type {Object} */ preview: null, /** * Start the engine. * * @since 1.9.2 */ init() { if ( window.wpforms_builder?.is_ai_disabled || app.isLoaded ) { return; } app.updateLocationUrl(); app.events(); app.isLoaded = true; }, /** * Events. * * @since 1.9.2 */ events() { $( document ) .on( 'wpformsSetupPanelBeforeInitTemplatesList', app.addTemplateCard ); $( '#wpforms-builder' ) .on( 'wpformsBuilderReady', app.maybeSaveForm ) .on( 'wpformsBuilderPanelLoaded', app.panelLoaded ); }, /** * Panel loaded event. * * @since 1.9.2 * * @param {Object} e Event object. * @param {string} panel Panel name. */ panelLoaded( e, panel ) { if ( panel !== 'setup' ) { return; } // Load generator modules and run the main module. Promise.all( [ import( strings.modules.main ), import( strings.modules.preview ), import( strings.modules.modals ), ] ) .then( ( [ moduleMain, modulePreview, moduleModals ] ) => { app.main = moduleMain.default( app, $ ); app.preview = modulePreview.default( app, $ ); app.modals = moduleModals.default( app, $ ); // Run the main module. app.main.init(); } ); }, /** * Add the generator template card to the list. * * At this point, before the list is rendered, we can add our card. * The card will be added to the top of the list. * Event handlers will be attached later by the main module. * * @since 1.9.2 */ addTemplateCard() { if ( $( '#wpforms-template-generate' ).length ) { return; } $( '#wpforms-setup-templates-list .list' ).prepend( app.renderTemplateCard() ); wpf.initTooltips( $( '#wpforms-template-generate .wpforms-template-buttons' ) ); }, /** * Render the template card HTML. * * @since 1.9.2 * * @return {string} The card markup. */ renderTemplateCard() { // eslint-disable-line complexity const cardClass = window.wpforms_builder?.template_slug === 'generate' ? 'selected' : ''; let buttonAttr = ''; let buttonClass = ! Object.keys( strings.addonsData ).length || strings.dismissed.installAddons ? 'wpforms-template-generate' : 'wpforms-template-generate-install-addons'; // In Lite, we should disable the button in the case Lite Connect is not allowed. if ( ! strings.isPro && ! strings.liteConnectAllowed ) { buttonClass += ' wpforms-inactive wpforms-help-tooltip wpforms-prevent-default'; buttonAttr = `data-tooltip-position="top" title="${ strings.templateCard.liteConnectNotAllowed }"`; } // In Lite, we should show the modal to enable Lite Connect if it is allowed. if ( ! strings.isPro && ! strings.liteConnectEnabled && strings.liteConnectAllowed ) { buttonClass += ' enable-lite-connect-modal wpforms-prevent-default'; } return `
    ${ strings.templateCard.name }

    ${ strings.templateCard.name }

    ${ strings.templateCard.new }

    ${ strings.templateCard.desc }

    `; }, /** * Save the form when the generated form opened. * * @since 1.9.2 */ maybeSaveForm() { // Only in case the generated form was used, we have a chat session in the localized vars. if ( wpforms_ai_chat_element.forms?.chatHtml && ! wpf.getQueryString( 'newform' ) ) { WPFormsBuilder.formSave( false ); } }, /** * Remove the session from URL. * * @since 1.9.2 */ updateLocationUrl() { history.replaceState( {}, null, wpf.updateQueryString( 'session', null ) ); }, }; return app; }( document, window, jQuery ) ); // Initialize. WPFormsAIFormGenerator.init(); integrations/ai/form-generator/form-generator.min.js000064400000005237151716470030016654 0ustar00var WPFormsAIFormGenerator=window.WPFormsAIFormGenerator||function(e,r,s){const l=wpforms_ai_form_generator,o={state:{},main:null,preview:null,init(){r.wpforms_builder?.is_ai_disabled||o.isLoaded||(o.updateLocationUrl(),o.events(),o.isLoaded=!0)},events(){s(e).on("wpformsSetupPanelBeforeInitTemplatesList",o.addTemplateCard),s("#wpforms-builder").on("wpformsBuilderReady",o.maybeSaveForm).on("wpformsBuilderPanelLoaded",o.panelLoaded)},panelLoaded(e,t){"setup"===t&&Promise.all([import(l.modules.main),import(l.modules.preview),import(l.modules.modals)]).then(([e,t,a])=>{o.main=e.default(o,s),o.preview=t.default(o,s),o.modals=a.default(o,s),o.main.init()})},addTemplateCard(){s("#wpforms-template-generate").length||(s("#wpforms-setup-templates-list .list").prepend(o.renderTemplateCard()),wpf.initTooltips(s("#wpforms-template-generate .wpforms-template-buttons")))},renderTemplateCard(){var e="generate"===r.wpforms_builder?.template_slug?"selected":"";let t="",a=!Object.keys(l.addonsData).length||l.dismissed.installAddons?"wpforms-template-generate":"wpforms-template-generate-install-addons";return l.isPro||l.liteConnectAllowed||(a+=" wpforms-inactive wpforms-help-tooltip wpforms-prevent-default",t=`data-tooltip-position="top" title="${l.templateCard.liteConnectNotAllowed}"`),l.isPro||l.liteConnectEnabled||!l.liteConnectAllowed||(a+=" enable-lite-connect-modal wpforms-prevent-default"),`
    ${l.templateCard.name}

    ${l.templateCard.name}

    ${l.templateCard.new}

    ${l.templateCard.desc}

    `},maybeSaveForm(){wpforms_ai_chat_element.forms?.chatHtml&&!wpf.getQueryString("newform")&&WPFormsBuilder.formSave(!1)},updateLocationUrl(){history.replaceState({},null,wpf.updateQueryString("session",null))}};return o}(document,window,jQuery);WPFormsAIFormGenerator.init();integrations/ai/form-generator/modules/main.js000064400000033406151716470030015536 0ustar00/* global wpf, wpforms_ai_form_generator, wpforms_ai_chat_element, WPFormsBuilder, wpforms_builder, WPFormsChallenge */ /** * @param strings.panel.backToTemplates * @param strings.panel.emptyStateDesc * @param strings.panel.emptyStateTitle * @param strings.templateCard.buttonTextContinue * @param wpforms_ai_chat_element.forms.responseHistory * @param wpforms_builder.template_slug */ /** * The WPForms AI form generator app. * * Main module. * * @since 1.9.2 * * @param {Object} generator The AI form generator. * @param {Object} $ jQuery function. * * @return {Object} The main module object. */ export default function( generator, $ ) { // eslint-disable-line max-lines-per-function /** * Localized strings. * * @since 1.9.2 * * @type {Object} */ const strings = wpforms_ai_form_generator; /** * The main module object. * * @since 1.9.2 */ const main = { /** * DOM elements. * * @since 1.9.2 */ el: {}, /** * Init generator. * * @since 1.9.2 */ init() { main.initState(); main.initElementsCache(); main.initStateProxy(); // Magic, we just need to set the state property to `true` to add the panel to the DOM. generator.state.panelAdd = true; generator.preview.init(); generator.modals.init(); main.events(); }, /** * Init generator state. * * @since 1.9.2 */ initState() { generator.state = { formId: $( '#wpforms-builder-form' ).data( 'id' ), panelAdd: false, panelOpen: false, chatStart: false, aiResponse: null, }; }, /** * Events. * * @since 1.9.2 */ events() { // Setup panel events. main.el.$setupPanel .on( 'click', '.wpforms-template-generate', main.event.clickGenerateFormBtn ) .on( 'click', '.wpforms-template-generate-install-addons', generator.modals.openAddonsModal ); // Generator panel events. main.el.$generatorPanel .on( 'click', '.wpforms-btn-back-to-templates', main.event.clickBackToTemplatesBtn ) .on( 'click', '.wpforms-ai-chat-reload-link', main.event.reloadPage ) .on( 'click', '.wpforms-ai-chat-use-form', main.event.useForm ); // The Form Builder events main.el.$builder .on( 'wpformsPanelSwitch', main.event.panelSwitch ); // AI chat events. main.el.$doc .on( 'wpformsBuilderReady', main.maybeOpenPanel ) .on( 'wpformsAIChatBeforeAddAnswer', main.event.chatBeforeAddAnswer ) .on( 'wpformsAIChatAddedAnswer', main.event.chatAddedAnswer ) .on( 'wpformsAIChatAfterRefresh', main.event.chatAfterRefresh ) .on( 'wpformsAIChatSetActiveAnswer', main.event.chatSetActiveAnswer ); }, /** * Init elements cache. * * @since 1.9.2 */ initElementsCache() { // Cache DOM elements. main.el.$doc = $( document ); main.el.$builder = $( '#wpforms-builder' ); main.el.$builderToolbar = $( '#wpforms-builder .wpforms-toolbar' ); main.el.$templatesList = $( '#wpforms-setup-templates-list .list' ); // The templates list container. main.el.$templateCard = $( '#wpforms-template-generate' ); // The generator template card. main.el.$generatorPanel = $( '#wpforms-panel-ai-form' ); // The generator panel. main.el.$setupPanel = $( '#wpforms-panel-setup' ); // The Setup panel. main.el.$panelsContainer = $( '.wpforms-panels' ); // All panels container. main.el.$allPanels = $( '.wpforms-panel' ); // All panels. main.el.$chat = main.el.$generatorPanel.find( 'wpforms-ai-chat .wpforms-ai-chat' ); // The chat container. }, /** * Init state proxy. * * @since 1.9.2 */ initStateProxy() { generator.state = new Proxy( generator.state, { set( state, key, value ) { // Set the state property. state[ key ] = value; if ( typeof main.setStateHandler[ key ] !== 'function' ) { return true; } // Run the set state property handler. main.setStateHandler[ key ]( value ); // Debug log. wpf.debug( 'Form Generator state changed:', key, '=', value ); return true; }, } ); }, /** * Event handlers * * @since 1.9.2 */ event: { /** * Click on the `Generate Form` button. * * @since 1.9.2 * * @param {Object} e Event object. */ clickGenerateFormBtn( e ) { e.preventDefault(); if ( $( this ).hasClass( 'wpforms-prevent-default' ) ) { return; } // Open the Form Generator panel. generator.state.panelOpen = true; }, /** * Click on the `Back to Templates` button. * * @since 1.9.2 */ clickBackToTemplatesBtn() { // Close the Form Generator panel. generator.state.panelOpen = false; }, /** * Before adding the answer to the chat. * * @since 1.9.2 * * @param {Object} e Event object. */ chatBeforeAddAnswer( e ) { // Store the AI response data in state. generator.state.aiResponse = e.originalEvent.detail?.response; generator.state.aiResponseHistory = generator.state.aiResponseHistory || {}; generator.state.aiResponseHistory[ generator.state.aiResponse?.responseId ] = generator.state.aiResponse; }, /** * The answer added to the chat. * * @since 1.9.2 * * @param {Object} e Event object. */ chatAddedAnswer( e ) { const chat = e.originalEvent.detail?.chat || {}; // Set chatStart state. if ( chat?.sessionId && ! generator.state.chatStart ) { generator.state.chatStart = true; } }, /** * Refresh the chat triggered. * * @since 1.9.2 */ chatAfterRefresh() { generator.preview.clear(); }, /** * Set active answer. Switch form preview to the active answer. * * @since 1.9.2 * * @param {Object} e Event object. */ chatSetActiveAnswer( e ) { generator.state.aiResponse = generator.state.aiResponseHistory[ e.originalEvent.detail?.responseId ]; }, /** * Click on the "use this form" button. * * @since 1.9.2 * * @param {Object} e Event object. */ useForm( e ) { e?.preventDefault(); const $button = $( this ); const formId = generator.state.formId; if ( ! formId || wpforms_builder.template_slug === 'generate' ) { main.useFormAjax( $button ); } else { generator.modals.openExistingFormModal( $button ); } }, /** * Click on the "reload" link. * * @since 1.9.2 * * @param {Object} e Event object. */ reloadPage( e ) { e?.preventDefault(); window.location = window.location + '&ai-form'; }, /** * Switch the Form Builder panel. * * @since 1.9.2 */ panelSwitch() { generator.state.panelOpen = false; }, }, /** * Set state property handlers. * * Each handler runs when the appropriate state property was set. * For example, when `panelAdd` state property was set, the `setStateHandler.panelAdd()` handler will run. * * @since 1.9.2 */ setStateHandler: { /** * `panelAdd` state handler. * * When the value is `true`, the panel will be added to the DOM, otherwise removed. * * @since 1.9.2 * * @param {boolean} value The state value. */ panelAdd( value ) { // Remove the panel from DOM. if ( ! value ) { main.el.$generatorPanel?.remove(); return; } // The panel already added, no need to add again. if ( main.el.$generatorPanel?.length ) { return; } // Add panel to DOM. main.el.$panelsContainer.append( main.render.generatorPanel() ); // Cache elements. main.el.$generatorPanel = $( '#wpforms-panel-ai-form' ); main.el.$chat = main.el.$generatorPanel.find( 'wpforms-ai-chat .wpforms-ai-chat' ); }, /** * Panel open state handler. * * @since 1.9.2 * * @param {boolean} value The state value. */ panelOpen( value ) { main.el.$generatorPanel.toggleClass( 'active', value ); main.el.$templateCard.addClass( 'selected' ); main.setToolbarState( value ); // Freeze/unfreeze the Challenge. window.WPFormsChallenge?.core.freezeChallenge( value, strings.misc.frozenChallengeTooltip ); $( 'body' ).toggleClass( 'wpforms-ai-form-generator-active', value ); if ( generator.state.aiResponseHistory || ! wpforms_ai_chat_element.forms.responseHistory ) { return; } // Update the response history if it exists. generator.state.aiResponseHistory = wpforms_ai_chat_element.forms.responseHistory; const $activeResponse = main.el.$chat.find( '.wpforms-chat-item-answer.active' ); const activeResponseId = $activeResponse.data( 'response-id' ); generator.state.aiResponse = generator.state.aiResponseHistory[ activeResponseId ]; // Scroll to the active response. $activeResponse[ 0 ].scrollIntoView( { behavior: 'smooth', block: 'end' } ); }, /** * Chat start state handler. * * @since 1.9.2 * * @param {boolean} value The state value. */ chatStart( value ) { if ( ! value ) { return; } // Update the generator template card button text. main.el.$templateCard .addClass( 'selected' ) .find( '.wpforms-template-generate' ) .text( strings.templateCard.buttonTextContinue ); }, /** * AI response state handler. * * @since 1.9.2 * * @param {Object} response The response data. */ aiResponse( response ) { if ( ! response ) { return; } // Update the preview. generator.preview.update(); }, /** * Is the form preview update in progress. * * @since 1.9.2 * * @param {boolean} value Flag value. */ isPreviewUpdate( value ) { main.el.$chat.toggleClass( 'wpforms-ai-chat-inactive', value ); }, }, /** * HTML renderers. * * @since 1.9.2 */ render: { /** * Render generator panel HTML. * * @since 1.9.2 * * @return {string} The panel markup. */ generatorPanel() { return `

    ${ strings.panel.emptyStateTitle }

    ${ strings.panel.emptyStateDesc }

    `; }, }, /** * Maybe open the form generator panel. * * @since 1.9.2 */ maybeOpenPanel() { // Open the panel only if the `ai-form` query string parameter exists. if ( ! window.location.search.includes( '&ai-form' ) ) { return; } // Remove the query string parameter from the URL. history.replaceState( {}, null, wpf.updateQueryString( 'ai-form', null ) ); // Open the LiteConnect modal if it is not enabled. const $buttonLiteConnect = $( '.wpforms-template-generate.enable-lite-connect-modal' ); if ( $buttonLiteConnect.length ) { setTimeout( function() { $buttonLiteConnect.trigger( 'click' ); }, 0 ); return; } // Open the panel if all addons are installed OR the modal is dismissed. if ( ! Object.keys( strings.addonsData ).length || strings.dismissed.installAddons ) { generator.state.panelOpen = true; return; } // Open the addons install modal. generator.modals.openAddonsModal( null ); }, /** * The "Use this form" ajax call. * * @since 1.9.2 * * @param {jQuery} $button Button element. */ useFormAjax( $button ) { const sessionId = $button.closest( '.wpforms-ai-chat' ).data( 'session-id' ); const responseId = $button.closest( '.wpforms-chat-item' ).data( 'response-id' ); WPFormsBuilder.showLoadingOverlay(); // Rate the response. main.getChatElement()?.wpformsAiApi.rate( true, responseId ); // Do not display the alert about unsaved changes. WPFormsBuilder.setCloseConfirmation( false ); const data = { action: 'wpforms_use_ai_form', nonce: strings.nonce, formId: generator.state.formId, formData: generator.state.aiResponseHistory[ responseId ], sessionId, chatHtml: $button.closest( 'wpforms-ai-chat' ).html(), responseHistory: generator.state.aiResponseHistory, }; generator.preview.closeTooltips(); $.post( strings.ajaxUrl, data ) .done( function( res ) { if ( ! res.success ) { wpf.debug( 'Form Generator AJAX error:', res.data.error ?? res.data ); return; } const newForm = ! data.formId ? '&newform=1' : ''; if ( ! window.WPFormsChallenge ) { window.location.assign( res.data.redirect + newForm ); return; } // When the Challenge is active, we need to resume it and continue the steps. WPFormsChallenge.core.resumeChallengeAndExec( {}, () => { WPFormsChallenge.core.stepCompleted( 2 ) .done( () => { window.location.assign( res.data.redirect + newForm ); } ); } ); } ) .fail( function( xhr ) { wpf.debug( 'Form Generator AJAX error:', xhr.responseText ?? xhr.statusText ); } ); }, /** * Set the Builder's toolbar state. * * @since 1.9.2 * * @param {boolean} isEmpty The toolbar is empty. */ setToolbarState( isEmpty ) { main.el.$builderToolbar.toggleClass( 'empty', isEmpty ); main.el.$builderToolbar.find( '#wpforms-help span' ).toggleClass( 'screen-reader-text', ! isEmpty ); }, /** * Get the AI chat element. * * @since 1.9.2 * * @return {HTMLElement} The chat element. */ getChatElement() { return main.el.$chat.parent()[ 0 ]; }, }; return main; } integrations/ai/form-generator/modules/main.min.js000064400000013725151716470030016322 0ustar00export default function(o,n){const r=wpforms_ai_form_generator,l={el:{},init(){l.initState(),l.initElementsCache(),l.initStateProxy(),o.state.panelAdd=!0,o.preview.init(),o.modals.init(),l.events()},initState(){o.state={formId:n("#wpforms-builder-form").data("id"),panelAdd:!1,panelOpen:!1,chatStart:!1,aiResponse:null}},events(){l.el.$setupPanel.on("click",".wpforms-template-generate",l.event.clickGenerateFormBtn).on("click",".wpforms-template-generate-install-addons",o.modals.openAddonsModal),l.el.$generatorPanel.on("click",".wpforms-btn-back-to-templates",l.event.clickBackToTemplatesBtn).on("click",".wpforms-ai-chat-reload-link",l.event.reloadPage).on("click",".wpforms-ai-chat-use-form",l.event.useForm),l.el.$builder.on("wpformsPanelSwitch",l.event.panelSwitch),l.el.$doc.on("wpformsBuilderReady",l.maybeOpenPanel).on("wpformsAIChatBeforeAddAnswer",l.event.chatBeforeAddAnswer).on("wpformsAIChatAddedAnswer",l.event.chatAddedAnswer).on("wpformsAIChatAfterRefresh",l.event.chatAfterRefresh).on("wpformsAIChatSetActiveAnswer",l.event.chatSetActiveAnswer)},initElementsCache(){l.el.$doc=n(document),l.el.$builder=n("#wpforms-builder"),l.el.$builderToolbar=n("#wpforms-builder .wpforms-toolbar"),l.el.$templatesList=n("#wpforms-setup-templates-list .list"),l.el.$templateCard=n("#wpforms-template-generate"),l.el.$generatorPanel=n("#wpforms-panel-ai-form"),l.el.$setupPanel=n("#wpforms-panel-setup"),l.el.$panelsContainer=n(".wpforms-panels"),l.el.$allPanels=n(".wpforms-panel"),l.el.$chat=l.el.$generatorPanel.find("wpforms-ai-chat .wpforms-ai-chat")},initStateProxy(){o.state=new Proxy(o.state,{set(e,t,a){return e[t]=a,"function"==typeof l.setStateHandler[t]&&(l.setStateHandler[t](a),wpf.debug("Form Generator state changed:",t,"=",a)),!0}})},event:{clickGenerateFormBtn(e){e.preventDefault(),n(this).hasClass("wpforms-prevent-default")||(o.state.panelOpen=!0)},clickBackToTemplatesBtn(){o.state.panelOpen=!1},chatBeforeAddAnswer(e){o.state.aiResponse=e.originalEvent.detail?.response,o.state.aiResponseHistory=o.state.aiResponseHistory||{},o.state.aiResponseHistory[o.state.aiResponse?.responseId]=o.state.aiResponse},chatAddedAnswer(e){(e.originalEvent.detail?.chat||{})?.sessionId&&!o.state.chatStart&&(o.state.chatStart=!0)},chatAfterRefresh(){o.preview.clear()},chatSetActiveAnswer(e){o.state.aiResponse=o.state.aiResponseHistory[e.originalEvent.detail?.responseId]},useForm(e){e?.preventDefault();e=n(this);o.state.formId&&"generate"!==wpforms_builder.template_slug?o.modals.openExistingFormModal(e):l.useFormAjax(e)},reloadPage(e){e?.preventDefault(),window.location=window.location+"&ai-form"},panelSwitch(){o.state.panelOpen=!1}},setStateHandler:{panelAdd(e){e?l.el.$generatorPanel?.length||(l.el.$panelsContainer.append(l.render.generatorPanel()),l.el.$generatorPanel=n("#wpforms-panel-ai-form"),l.el.$chat=l.el.$generatorPanel.find("wpforms-ai-chat .wpforms-ai-chat")):l.el.$generatorPanel?.remove()},panelOpen(e){var t;l.el.$generatorPanel.toggleClass("active",e),l.el.$templateCard.addClass("selected"),l.setToolbarState(e),window.WPFormsChallenge?.core.freezeChallenge(e,r.misc.frozenChallengeTooltip),n("body").toggleClass("wpforms-ai-form-generator-active",e),!o.state.aiResponseHistory&&wpforms_ai_chat_element.forms.responseHistory&&(o.state.aiResponseHistory=wpforms_ai_chat_element.forms.responseHistory,t=(e=l.el.$chat.find(".wpforms-chat-item-answer.active")).data("response-id"),o.state.aiResponse=o.state.aiResponseHistory[t],e[0].scrollIntoView({behavior:"smooth",block:"end"}))},chatStart(e){e&&l.el.$templateCard.addClass("selected").find(".wpforms-template-generate").text(r.templateCard.buttonTextContinue)},aiResponse(e){e&&o.preview.update()},isPreviewUpdate(e){l.el.$chat.toggleClass("wpforms-ai-chat-inactive",e)}},render:{generatorPanel(){return`

    ${r.panel.emptyStateTitle}

    ${r.panel.emptyStateDesc}

    `}},maybeOpenPanel(){if(window.location.search.includes("&ai-form")){history.replaceState({},null,wpf.updateQueryString("ai-form",null));const e=n(".wpforms-template-generate.enable-lite-connect-modal");e.length?setTimeout(function(){e.trigger("click")},0):!Object.keys(r.addonsData).length||r.dismissed.installAddons?o.state.panelOpen=!0:o.modals.openAddonsModal(null)}},useFormAjax(e){var t=e.closest(".wpforms-ai-chat").data("session-id"),a=e.closest(".wpforms-chat-item").data("response-id");WPFormsBuilder.showLoadingOverlay(),l.getChatElement()?.wpformsAiApi.rate(!0,a),WPFormsBuilder.setCloseConfirmation(!1);const s={action:"wpforms_use_ai_form",nonce:r.nonce,formId:o.state.formId,formData:o.state.aiResponseHistory[a],sessionId:t,chatHtml:e.closest("wpforms-ai-chat").html(),responseHistory:o.state.aiResponseHistory};o.preview.closeTooltips(),n.post(r.ajaxUrl,s).done(function(e){if(e.success){const t=s.formId?"":"&newform=1";window.WPFormsChallenge?WPFormsChallenge.core.resumeChallengeAndExec({},()=>{WPFormsChallenge.core.stepCompleted(2).done(()=>{window.location.assign(e.data.redirect+t)})}):window.location.assign(e.data.redirect+t)}else wpf.debug("Form Generator AJAX error:",e.data.error??e.data)}).fail(function(e){wpf.debug("Form Generator AJAX error:",e.responseText??e.statusText)})},setToolbarState(e){l.el.$builderToolbar.toggleClass("empty",e),l.el.$builderToolbar.find("#wpforms-help span").toggleClass("screen-reader-text",!e)},getChatElement(){return l.el.$chat.parent()[0]}};return l}integrations/ai/form-generator/modules/modals.js000064400000022756151716470030016077 0ustar00/* global wpforms_ai_form_generator, wpf, WPFormsBuilder, wpforms_builder */ /** * @param strings.addonsAction * @param strings.addonsData * @param strings.addons.installTitle * @param strings.addons.installContent * @param strings.addons.activateContent * @param strings.addons.installButton * @param strings.addons.installConfirmButton * @param strings.addons.activateConfirmButton * @param strings.addons.cancelButton * @param strings.addons.dontShow * @param strings.addons.dismissErrorTitle * @param strings.addons.dismissError * @param strings.addons.addonsInstalledTitle * @param strings.addons.addonsActivatedTitle * @param strings.addons.addonsInstalledContent * @param strings.addons.okay * @param strings.addons.addonsInstallErrorTitle * @param strings.addons.addonsActivateErrorTitle * @param strings.addons.addonsInstallError * @param strings.addons.addonsInstallErrorNetwork * @param strings.adminNonce * @param strings.misc.warningExistingForm * @param this.$$confirm * @param this.$$cancel */ /** * The WPForms AI form generator app. * * Modal windows' module. * * @since 1.9.2 * * @param {Object} generator The AI form generator. * @param {Object} $ jQuery function. * * @return {Object} The preview module object. */ export default function( generator, $ ) { // eslint-disable-line max-lines-per-function /** * Localized strings. * * @since 1.9.2 * * @type {Object} */ const strings = wpforms_ai_form_generator; /** * The preview module object. * * @since 1.9.2 */ const modals = { /** * DOM elements. * * @since 1.9.2 */ el: {}, /** * AJAX error debug string. * * @since 1.9.2 */ ajaxError: 'Form Generator AJAX error:', /** * Init generator. * * @since 1.9.2 */ init() { modals.el.$doc = $( document ); modals.el.$templateCard = $( '#wpforms-template-generate' ); modals.events(); }, /** * Register events. */ events() { modals.el.$doc.on( 'change', '.wpforms-ai-forms-install-addons-modal-dismiss', modals.dismissAddonsModal ); }, /** * Open the addons modal. * * @since 1.9.2 * * @param {Object} e Event object. */ openAddonsModal( e ) { // eslint-disable-line max-lines-per-function e?.preventDefault(); const spinner = ''; const isInstall = strings.addonsAction === 'install'; const content = isInstall ? strings.addons.installContent : strings.addons.activateContent; const options = { title: strings.addons.installTitle, content, type: 'purple', icon: 'fa fa-info-circle', buttons: { confirm: { text: isInstall ? strings.addons.installConfirmButton : strings.addons.activateConfirmButton, btnClass: 'btn-confirm', keys: [ 'enter' ], action() { const label = isInstall ? strings.addons.installing : strings.addons.activating; this.$$confirm.prop( 'disabled', true ).html( spinner + label ); this.$$cancel.prop( 'disabled', true ); modals.installAddonsAjax( this ); return false; }, }, cancel: { text: strings.addons.cancelButton, keys: [ 'esc' ], btnClass: 'btn-cancel', action() { modals.updateGenerateFormButton( false ); // Open the Form Generator panel. setTimeout( () => { generator.state.panelOpen = true; }, 250 ); }, }, }, onOpenBefore() { // Add the checkbox to the modal. const dontShowAgain = ` `; this.$body .addClass( 'wpforms-ai-forms-install-addons-modal' ) .find( '.jconfirm-buttons' ) .after( dontShowAgain ); }, }; $.confirm( options ); }, /** * Install required addons AJAX. * * @since 1.9.2 * * @param {Object} previousModal Previous modal instance. */ installAddonsAjax( previousModal ) { // eslint-disable-line max-lines-per-function let chain = null; let errorDisplayed = false; const postDone = function( res ) { if ( ! res.success ) { wpf.debug( modals.ajaxError, res.data.error ?? res.data ); } if ( ! res.success && ! errorDisplayed ) { errorDisplayed = true; modals.openErrorModal( { title: strings.addonsAction === 'install' ? strings.addons.addonsInstallErrorTitle : strings.addons.addonsActivateErrorTitle, content: strings.addons.addonsInstallError, } ); } }; const postFail = function( xhr ) { if ( errorDisplayed ) { return; } const error = xhr.responseText || strings.addons.addonsInstallErrorNetwork; let content = strings.addons.addonsInstallError; content += error && error !== 'error' ? '
    ' + error : ''; wpf.debug( modals.ajaxError, content ); modals.openErrorModal( { title: strings.addonsAction === 'install' ? strings.addons.addonsInstallErrorTitle : strings.addons.addonsActivateErrorTitle, content, } ); errorDisplayed = true; }; // Do not display the alert about unsaved changes. WPFormsBuilder.setCloseConfirmation( false ); // Loop through all addons and make a chained AJAX calls. for ( const slug in strings.addonsData ) { const url = strings.addonsData[ slug ]?.url; const data = { action: url ? 'wpforms_install_addon' : 'wpforms_activate_addon', nonce : strings.adminNonce, plugin: url ? url : strings.addonsData[ slug ]?.path, type : 'addon', }; if ( chain === null ) { chain = $.post( strings.ajaxUrl, data, postDone ); } else { chain = chain.then( () => { return $.post( strings.ajaxUrl, data, postDone ); } ); } chain.fail( postFail ); } // Open the Addons Installed modal after the last AJAX call. chain .then( () => { if ( ! errorDisplayed ) { modals.openAddonsInstalledModal(); } } ) .always( () => { previousModal.close(); modals.updateGenerateFormButton( false ); } ); }, /** * Dismiss or de-dismiss element. * * @since 1.9.2 */ dismissAddonsModal() { const $checkbox = $( this ); const isChecked = $checkbox.prop( 'checked' ); const data = { action: 'wpforms_dismiss_ai_form', nonce: strings.nonce, element: 'install-addons-modal', dismiss: isChecked, }; modals.updateGenerateFormButton( ! isChecked ); $.post( strings.ajaxUrl, data ) .done( function( res ) { if ( res.success ) { return; } modals.openErrorModal( { title: strings.addons.dismissErrorTitle, content: strings.addons.dismissError, } ); wpf.debug( modals.ajaxError, res.data.error ?? res.data ); } ) .fail( function( xhr ) { modals.openErrorModal( { title: strings.addons.dismissErrorTitle, content: strings.addons.dismissError + '
    ' + strings.addons.addonsInstallErrorNetwork, } ); wpf.debug( modals.ajaxError, xhr.responseText ?? xhr.statusText ); } ); }, /** * Update the Generate Form button to enable/disable install addons modal window. * * @since 1.9.2 * * @param {boolean} shouldInstallAddons Should open install addons modal. */ updateGenerateFormButton( shouldInstallAddons ) { if ( shouldInstallAddons ) { $( '.wpforms-template-generate' ) .removeClass( 'wpforms-template-generate' ) .addClass( 'wpforms-template-generate-install-addons' ); } else { $( '.wpforms-template-generate-install-addons' ) .removeClass( 'wpforms-template-generate-install-addons' ) .addClass( 'wpforms-template-generate' ); } }, /** * Open the Addons Installed modal. * * @since 1.9.2 */ openAddonsInstalledModal() { const options = { title: strings.addonsAction === 'install' ? strings.addons.addonsInstalledTitle : strings.addons.addonsActivatedTitle, content: strings.addons.addonsInstalledContent, icon: 'fa fa-check-circle', type: 'green', buttons: { confirm: { text: strings.addons.okay, btnClass: 'btn-confirm', keys: [ 'enter' ], action() { WPFormsBuilder.showLoadingOverlay(); window.location = window.location + '&ai-form'; }, }, }, onOpenBefore() { this.$body .addClass( 'wpforms-ai-forms-addons-installed-modal' ); }, }; $.confirm( options ); }, /** * Warning for the existing form. * * @since 1.9.2 * * @param {jQuery} $button The "Use This Form" button. */ openExistingFormModal( $button ) { $.confirm( { title: wpforms_builder.heads_up, content: strings.misc.warningExistingForm, icon: 'fa fa-exclamation-circle', type: 'orange', buttons: { confirm: { text: wpforms_builder.ok, btnClass: 'btn-confirm', keys: [ 'enter' ], action() { generator.main.useFormAjax( $button ); }, }, cancel: { text: wpforms_builder.cancel, }, }, } ); }, /** * Open the error modal. * * @since 1.9.2 * * @param {Object} args Arguments. */ openErrorModal( args ) { const options = { title: args.title ?? false, content: args.content ?? false, icon: 'fa fa-exclamation-circle', type: 'red', buttons: { confirm: { text: strings.addons.okay, btnClass: 'btn-confirm', keys: [ 'enter' ], }, }, }; $.confirm( options ); }, }; return modals; } integrations/ai/form-generator/modules/modals.min.js000064400000010144151716470030016645 0ustar00export default function(t,l){const i=wpforms_ai_form_generator,c={el:{},ajaxError:"Form Generator AJAX error:",init(){c.el.$doc=l(document),c.el.$templateCard=l("#wpforms-template-generate"),c.events()},events(){c.el.$doc.on("change",".wpforms-ai-forms-install-addons-modal-dismiss",c.dismissAddonsModal)},openAddonsModal(n){n?.preventDefault();const o="install"===i.addonsAction;n=o?i.addons.installContent:i.addons.activateContent,n={title:i.addons.installTitle,content:n,type:"purple",icon:"fa fa-info-circle",buttons:{confirm:{text:o?i.addons.installConfirmButton:i.addons.activateConfirmButton,btnClass:"btn-confirm",keys:["enter"],action(){var n=o?i.addons.installing:i.addons.activating;return this.$$confirm.prop("disabled",!0).html(''+n),this.$$cancel.prop("disabled",!0),c.installAddonsAjax(this),!1}},cancel:{text:i.addons.cancelButton,keys:["esc"],btnClass:"btn-cancel",action(){c.updateGenerateFormButton(!1),setTimeout(()=>{t.state.panelOpen=!0},250)}}},onOpenBefore(){var n=` `;this.$body.addClass("wpforms-ai-forms-install-addons-modal").find(".jconfirm-buttons").after(n)}};l.confirm(n)},installAddonsAjax(n){let o=null,t=!1;function a(n){n.success||wpf.debug(c.ajaxError,n.data.error??n.data),n.success||t||(t=!0,c.openErrorModal({title:"install"===i.addonsAction?i.addons.addonsInstallErrorTitle:i.addons.addonsActivateErrorTitle,content:i.addons.addonsInstallError}))}function e(n){var o;t||(n=n.responseText||i.addons.addonsInstallErrorNetwork,o=i.addons.addonsInstallError,o+=n&&"error"!==n?"
    "+n:"",wpf.debug(c.ajaxError,o),c.openErrorModal({title:"install"===i.addonsAction?i.addons.addonsInstallErrorTitle:i.addons.addonsActivateErrorTitle,content:o}),t=!0)}WPFormsBuilder.setCloseConfirmation(!1);for(const d in i.addonsData){var s=i.addonsData[d]?.url;const r={action:s?"wpforms_install_addon":"wpforms_activate_addon",nonce:i.adminNonce,plugin:s||i.addonsData[d]?.path,type:"addon"};(o=null===o?l.post(i.ajaxUrl,r,a):o.then(()=>l.post(i.ajaxUrl,r,a))).fail(e)}o.then(()=>{t||c.openAddonsInstalledModal()}).always(()=>{n.close(),c.updateGenerateFormButton(!1)})},dismissAddonsModal(){var n=l(this).prop("checked"),o={action:"wpforms_dismiss_ai_form",nonce:i.nonce,element:"install-addons-modal",dismiss:n};c.updateGenerateFormButton(!n),l.post(i.ajaxUrl,o).done(function(n){n.success||(c.openErrorModal({title:i.addons.dismissErrorTitle,content:i.addons.dismissError}),wpf.debug(c.ajaxError,n.data.error??n.data))}).fail(function(n){c.openErrorModal({title:i.addons.dismissErrorTitle,content:i.addons.dismissError+"
    "+i.addons.addonsInstallErrorNetwork}),wpf.debug(c.ajaxError,n.responseText??n.statusText)})},updateGenerateFormButton(n){n?l(".wpforms-template-generate").removeClass("wpforms-template-generate").addClass("wpforms-template-generate-install-addons"):l(".wpforms-template-generate-install-addons").removeClass("wpforms-template-generate-install-addons").addClass("wpforms-template-generate")},openAddonsInstalledModal(){var n={title:"install"===i.addonsAction?i.addons.addonsInstalledTitle:i.addons.addonsActivatedTitle,content:i.addons.addonsInstalledContent,icon:"fa fa-check-circle",type:"green",buttons:{confirm:{text:i.addons.okay,btnClass:"btn-confirm",keys:["enter"],action(){WPFormsBuilder.showLoadingOverlay(),window.location=window.location+"&ai-form"}}},onOpenBefore(){this.$body.addClass("wpforms-ai-forms-addons-installed-modal")}};l.confirm(n)},openExistingFormModal(n){l.confirm({title:wpforms_builder.heads_up,content:i.misc.warningExistingForm,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"],action(){t.main.useFormAjax(n)}},cancel:{text:wpforms_builder.cancel}}})},openErrorModal(n){n={title:n.title??!1,content:n.content??!1,icon:"fa fa-exclamation-circle",type:"red",buttons:{confirm:{text:i.addons.okay,btnClass:"btn-confirm",keys:["enter"]}}};l.confirm(n)}};return c}integrations/ai/form-generator/modules/preview.js000064400000026525151716470030016277 0ustar00/* global wpforms_ai_form_generator, wpf, wpforms_addons */ /** * @param strings.dismissed.previewNotice * @param strings.licenseType * @param strings.previewNotice.btnUpgrade * @param strings.previewNotice.msgUpgrade * @param wpforms_ai_form_generator.addonFields */ /** * The WPForms AI form generator app. * * Form preview module. * * @since 1.9.2 * * @param {Object} generator The AI form generator. * @param {Object} $ jQuery function. * * @return {Object} The preview module object. */ export default function( generator, $ ) { // eslint-disable-line max-lines-per-function /** * Localized strings. * * @since 1.9.2 * * @type {Object} */ const strings = wpforms_ai_form_generator; /** * The preview module object. * * @since 1.9.2 */ const preview = { /** * DOM elements. * * @since 1.9.2 */ el: {}, /** * Mouse coordinates. * * @since 1.9.2 */ mouse: {}, /** * Init module. * * @since 1.9.2 */ init() { preview.el.$contentWrap = generator.main.el.$generatorPanel.find( '.wpforms-panel-content-wrap' ); preview.el.$content = preview.el.$contentWrap.find( '.wpforms-panel-content' ); preview.el.$emptyState = preview.el.$content.find( '.wpforms-panel-empty-state' ); preview.events(); }, /** * Preview events. * * @since 1.9.2 */ events() { // Track mouse coordinates. $( document ).on( 'mousemove', ( e ) => { preview.mouse.x = e.pageX; preview.mouse.y = e.pageY; } ); preview.el.$contentWrap.on( 'scroll', preview.closeTooltips ); }, /** * Update the preview according to the response stored in the generator state. * * @since 1.9.2 */ update() { // eslint-disable-line complexity /** * @param response.fieldsOrder.length * @param response.settings.submit_text */ const response = generator.state.aiResponse; if ( ! response || ! response.fields ) { return; } // Set the preview update flag. generator.state.isPreviewUpdate = true; // Reset preview fields. Here we will store the field ids that where added to the preview. generator.state.previewFields = []; // Remove existing fields and hide empty state. preview.clear( false ); // Display the form header. preview.displayHeader( response ); for ( const key in response.fieldsOrder ) { const fieldId = response.fieldsOrder[ key ]; preview.field( response.fields[ fieldId ], key ); } // Add submit button. if ( response.fieldsOrder?.length ) { preview.displaySubmit( response.settings?.submit_text || strings.panel.submitButton ); return; } // Show the empty state if there are no fields. preview.el.$emptyState.removeClass( 'wpforms-hidden-strict' ); generator.state.isPreviewUpdate = false; }, /** * A single field preview. * * @since 1.9.2 * * @param {Object} fieldSettings Field settings. * @param {number} key Field key. */ async field( fieldSettings, key ) { // Add a field placeholder to the preview. const html = `
    `; preview.el.$content.append( html ); const data = { action: 'wpforms_get_ai_form_field_preview', nonce: strings.nonce, field: fieldSettings, }; // Delay the AJAX request to simulate one-by-one field loading. await preview.delay( 300 * key ); // Field preview AJAX request. $.post( strings.ajaxUrl, data ) .done( function( res ) { if ( ! res.success ) { wpf.debug( 'Form Generator AJAX error:', res.data.error ?? res.data ); return; } preview.displayField( res.data ?? '', fieldSettings ); } ) .fail( function( xhr ) { wpf.debug( 'Form Generator AJAX error:', xhr.responseText ?? xhr.statusText ); } ); }, /** * Display the field in his placeholder. * * @since 1.9.2 * * @param {string} fieldHtml Field HTML. * @param {Object} fieldSettings Field settings. */ displayField( fieldHtml, fieldSettings ) { if ( ! fieldSettings.id && fieldSettings.id !== 0 ) { return; } const $fieldBlock = preview.el.$content.find( '#wpforms-generator-field-' + fieldSettings.id ); const $field = $fieldBlock.find( '.wpforms-field' ); const $placeholder = $fieldBlock.find( '.placeholder' ); $placeholder .addClass( 'fade-out' ); $field .html( fieldHtml ?? '' ) .addClass( 'fade-in' ) .toggleClass( 'wpforms-hidden', ! fieldHtml ) // Hide preview if the field is empty. .toggleClass( 'required', fieldSettings.required === '1' ) // Display the required field mark (asterisk) on the field label. .toggleClass( 'label_empty', ! fieldSettings.label ); // The field with an empty label. preview.initTooltip( $field ); preview.initPageBreak( $field, fieldSettings ); generator.state.previewFields.push( fieldSettings.id ); // Detect whether all the fields are loaded. if ( generator.state.previewFields.length !== Object.keys( generator.state.aiResponse?.fields ).length ) { return; } generator.state.isPreviewUpdate = false; preview.displayAddonsNotice(); }, /** * Display the missing addons notice. * * @since 1.9.4 */ displayAddonsNotice() { if ( strings.dismissed.previewNotice ) { return; } if ( ! [ 'basic', 'plus' ].includes( strings.licenseType ) ) { return; } const addons = preview.getAddonsUsedInResponse(); if ( ! addons.length ) { return; } const title = strings.previewNotice.title + ' ' + addons; const button = strings.previewNotice.btnUpgrade; const eduAttr = `data-action="upgrade" data-name="${ strings.previewNotice.addons }" data-utm-content="AI Form Generator"`; const text = strings.previewNotice.msgUpgrade .replace( 'href="#"', `href="#" class="education-modal" ${ eduAttr }` ); preview.el.$content .append( `

    ${ title }

    ${ text }

    ` ); }, /** * Get addons used in AI response. * * @since 1.9.4 * * @return {string} Addons used in the response. */ getAddonsUsedInResponse() { // eslint-disable-line complexity const response = generator.state.aiResponse; if ( ! response || ! response.fields ) { return ''; } const addons = []; for ( const key in response.fields ) { const addon = wpforms_ai_form_generator.addonFields[ response.fields[ key ].type ]; if ( ! addon ) { continue; } const addonName = wpforms_addons[ 'wpforms-' + addon ]?.title.replace( strings.addons.addon, '' ).trim(); if ( ! addonName || addons.includes( addonName ) ) { continue; } addons.push( addonName ); } if ( ! addons.length ) { return ''; } let lastAddon = addons.pop(); lastAddon += ' ' + strings.addons.addon; return addons.length ? addons.join( ', ' ) + ', ' + strings.addons.and + ' ' + lastAddon : lastAddon; }, /** * Init the page breaks. * * @since 1.9.2 * * @param {jQuery} $field Field jQuery object. * @param {Object} fieldSettings Field settings. */ initPageBreak( $field, fieldSettings ) { if ( fieldSettings.type === 'pagebreak' && ! [ 'top', 'bottom' ].includes( fieldSettings.position ) ) { $field.addClass( 'wpforms-pagebreak-normal' ); } }, /** * Init the preview tooltip. * * @since 1.9.2 * * @param {jQuery} $field Field jQuery object. */ initTooltip( $field ) { const width = 260; const args = { content: strings.panel.tooltipTitle + '
    ' + strings.panel.tooltipText, trigger: 'manual', interactive: true, animationDuration: 100, delay: 0, side: [ 'top' ], contentAsHTML: true, functionPosition: ( instance, helper, position ) => { // Set the tooltip position based on the mouse coordinates. position.coord.top = preview.mouse.y - 57; position.coord.left = preview.mouse.x - ( width / 2 ); return position; }, }; // Initialize. $field.tooltipster( args ); preview.toggleTooltipOnClick( $field ); }, /** * Toggle the preview tooltip on click. * * @since 1.9.2 * * @param {jQuery} $field Field jQuery object. */ toggleTooltipOnClick( $field ) { $field.on( 'click', () => { // Close opened tooltips on other fields. preview.closeTooltips(); const status = $field.tooltipster( 'status' ); $field.tooltipster( status.state === 'closed' ? 'open' : 'close' ); if ( status.state !== 'closed' ) { return; } const instance = $field.tooltipster( 'instance' ); // Adjust tooltip styling. instance._$tooltip.css( { height: 'auto', } ); instance._$tooltip.find( '.tooltipster-arrow' ).css( { left: '50%', } ); // Close the tooltip after 5 seconds. setTimeout( function() { preview.closeTooltips(); }, 5000 ); } ); }, /** * Close tooltips. * * @since 1.9.2 */ closeTooltips() { preview.el.$content.find( '.wpforms-field' ).each( function() { const $this = $( this ); if ( $this.hasClass( 'tooltipstered' ) && $this.parent().length ) { $this.tooltipster( 'close' ); } } ); }, /** * Display the form header. * * @since 1.9.4 * * @param {Object} response Button text. */ displayHeader( response ) { const title = `

    ${ response.form_title ?? '' }

    `; // Add form title. preview.el.$content.prepend( title ); }, /** * Display the `submit` button. * * @since 1.9.2 * * @param {string} label Button text. */ displaySubmit( label ) { preview.el.$content .append( `` ); }, /** * Clear the preview content. * * @since 1.9.2 * * @param {boolean} isEmptyState Whether to show the empty state or not. */ clear( isEmptyState = true ) { preview.el.$content.find( '.wpforms-ai-form-generator-preview-field' ).remove(); preview.el.$content.find( '.wpforms-ai-form-generator-preview-placeholder' ).remove(); preview.el.$content.find( '.wpforms-ai-form-generator-preview-title' ).remove(); preview.el.$content.find( '.wpforms-ai-form-generator-preview-addons-notice' ).remove(); preview.el.$content.find( '.wpforms-ai-form-generator-preview-submit' ).remove(); preview.el.$emptyState.toggleClass( 'wpforms-hidden-strict', ! isEmptyState ); }, /** * Delay promise. * * @since 1.9.2 * * @param {number} time Time in milliseconds. * * @return {Promise} Promise. */ delay( time ) { return new Promise( ( res ) => { setTimeout( res, time ); } ); }, }; return preview; } integrations/ai/form-generator/modules/preview.min.js000064400000011565151716470030017057 0ustar00export default function(r,s){const a=wpforms_ai_form_generator,i={el:{},mouse:{},init(){i.el.$contentWrap=r.main.el.$generatorPanel.find(".wpforms-panel-content-wrap"),i.el.$content=i.el.$contentWrap.find(".wpforms-panel-content"),i.el.$emptyState=i.el.$content.find(".wpforms-panel-empty-state"),i.events()},events(){s(document).on("mousemove",e=>{i.mouse.x=e.pageX,i.mouse.y=e.pageY}),i.el.$contentWrap.on("scroll",i.closeTooltips)},update(){var e=r.state.aiResponse;if(e&&e.fields){r.state.isPreviewUpdate=!0,r.state.previewFields=[],i.clear(!1),i.displayHeader(e);for(const o in e.fieldsOrder){var t=e.fieldsOrder[o];i.field(e.fields[t],o)}e.fieldsOrder?.length?i.displaySubmit(e.settings?.submit_text||a.panel.submitButton):(i.el.$emptyState.removeClass("wpforms-hidden-strict"),r.state.isPreviewUpdate=!1)}},async field(t,e){var o=`
    `,o=(i.el.$content.append(o),{action:"wpforms_get_ai_form_field_preview",nonce:a.nonce,field:t});await i.delay(300*e),s.post(a.ajaxUrl,o).done(function(e){e.success?i.displayField(e.data??"",t):wpf.debug("Form Generator AJAX error:",e.data.error??e.data)}).fail(function(e){wpf.debug("Form Generator AJAX error:",e.responseText??e.statusText)})},displayField(e,t){var o,s;!t.id&&0!==t.id||(s=(o=i.el.$content.find("#wpforms-generator-field-"+t.id)).find(".wpforms-field"),o.find(".placeholder").addClass("fade-out"),s.html(e??"").addClass("fade-in").toggleClass("wpforms-hidden",!e).toggleClass("required","1"===t.required).toggleClass("label_empty",!t.label),i.initTooltip(s),i.initPageBreak(s,t),r.state.previewFields.push(t.id),r.state.previewFields.length!==Object.keys(r.state.aiResponse?.fields).length)||(r.state.isPreviewUpdate=!1,i.displayAddonsNotice())},displayAddonsNotice(){var e,t,o,s;a.dismissed.previewNotice||["basic","plus"].includes(a.licenseType)&&(e=i.getAddonsUsedInResponse()).length&&(e=a.previewNotice.title+" "+e,t=a.previewNotice.btnUpgrade,o=`data-action="upgrade" data-name="${a.previewNotice.addons}" data-utm-content="AI Form Generator"`,s=a.previewNotice.msgUpgrade.replace('href="#"','href="#" class="education-modal" '+o),i.el.$content.append(`

    ${e}

    ${s}

    `))},getAddonsUsedInResponse(){var e=r.state.aiResponse;if(!e||!e.fields)return"";var t,o=[];for(const i in e.fields){var s=wpforms_ai_form_generator.addonFields[e.fields[i].type];s&&(s=wpforms_addons["wpforms-"+s]?.title.replace(a.addons.addon,"").trim())&&!o.includes(s)&&o.push(s)}return o.length?(t=o.pop(),t+=" "+a.addons.addon,o.length?o.join(", ")+", "+a.addons.and+" "+t:t):""},initPageBreak(e,t){"pagebreak"!==t.type||["top","bottom"].includes(t.position)||e.addClass("wpforms-pagebreak-normal")},initTooltip(e){var t={content:a.panel.tooltipTitle+"
    "+a.panel.tooltipText,trigger:"manual",interactive:!0,animationDuration:100,delay:0,side:["top"],contentAsHTML:!0,functionPosition:(e,t,o)=>(o.coord.top=i.mouse.y-57,o.coord.left=i.mouse.x-130,o)};e.tooltipster(t),i.toggleTooltipOnClick(e)},toggleTooltipOnClick(t){t.on("click",()=>{i.closeTooltips();var e=t.tooltipster("status");t.tooltipster("closed"===e.state?"open":"close"),"closed"===e.state&&((e=t.tooltipster("instance"))._$tooltip.css({height:"auto"}),e._$tooltip.find(".tooltipster-arrow").css({left:"50%"}),setTimeout(function(){i.closeTooltips()},5e3))})},closeTooltips(){i.el.$content.find(".wpforms-field").each(function(){var e=s(this);e.hasClass("tooltipstered")&&e.parent().length&&e.tooltipster("close")})},displayHeader(e){e=`

    ${e.form_title??""}

    `;i.el.$content.prepend(e)},displaySubmit(e){i.el.$content.append(``)},clear(e=!0){i.el.$content.find(".wpforms-ai-form-generator-preview-field").remove(),i.el.$content.find(".wpforms-ai-form-generator-preview-placeholder").remove(),i.el.$content.find(".wpforms-ai-form-generator-preview-title").remove(),i.el.$content.find(".wpforms-ai-form-generator-preview-addons-notice").remove(),i.el.$content.find(".wpforms-ai-form-generator-preview-submit").remove(),i.el.$emptyState.toggleClass("wpforms-hidden-strict",!e)},delay(t){return new Promise(e=>{setTimeout(e,t)})}};return i}integrations/constant-contact-v3/auth.js000064400000012314151716470030014366 0ustar00/* global wpf, WPFormsBuilder, WPFormsConstantContactV3AuthVars */ /** * @param window.wpforms_admin * @param window.wpforms_builder * @param WPFormsConstantContactV3AuthVars.auth_url */ /** * WPForms Constant Contact V3 Popup. * * @since 1.9.3 */ const WPFormsConstantContactV3Auth = window.WPFormsConstantContactV3Auth || ( function( document, window, $ ) { /** * Public functions and properties. * * @since 1.9.3 * * @type {Object} */ const app = { /** * Is the authorization window opened? * * @since 1.9.3 */ isOpened : false, /** * URL to listen for messages from the window. * * @since 1.9.3 */ listenURL: '', /** * Start the engine. * * @since 1.9.3 */ init: () => { $( app.ready ); }, /** * Document ready. * * @since 1.9.3 */ ready: () => { const redirectUri = new URL( WPFormsConstantContactV3AuthVars.auth_url ).searchParams.get( 'redirect_uri' ); app.listenURL = new URL( redirectUri ).origin; $( document ) .on( 'click', '.wpforms-constant-contact-v3-auth, .wpforms-builder-constant-contact-v3-provider-sign-up', app.showWindow ) .on( 'click', '#wpforms-settings-constant-contact-v3-migration-prompt-link', app.promptMigration ); }, /** * Show a window. * * @since 1.9.3 * * @param {Event} e Click event. */ showWindow: ( e ) => { e.preventDefault(); if ( app.isOpened ) { return; } const authUrl = WPFormsConstantContactV3AuthVars.auth_url, width = 500, height = 600, left = ( screen.width / 2 ) - ( width / 2 ), top = ( screen.height / 2 ) - ( height / 2 ), loginHintEmail = $( '.wpforms-constant-contact-v3-auth' ).data( 'login-hint' ), url = new URL( authUrl ); if ( loginHintEmail ) { url.searchParams.set( 'login_hint', loginHintEmail ); } const newWindow = window.open( url.toString(), 'authPopup', 'width=' + width + ', height=' + height + ', top=' + top + ', left=' + left ); window.addEventListener( 'message', app.listenResponse ); const checkWindowClosed = setInterval( () => { if ( newWindow.closed ) { clearInterval( checkWindowClosed ); app.isOpened = false; } }, 1000 ); app.isOpened = true; }, /** * Listen for response. * * @since 1.9.3 * * @param {Event} event Message event. */ listenResponse: ( event ) => { if ( event.origin !== app.listenURL ) { return; } if ( ! event.data ) { app.errorModal( WPFormsConstantContactV3AuthVars.strings.error ); return; } app.saveAccount( event.data ); }, /** * Save account. * * @since 1.9.3 * * @param {string} code Authorization code. */ saveAccount: ( code ) => { const modal = app.waitModal(); $.post( WPFormsConstantContactV3AuthVars.ajax_url, { action: 'wpforms_constant_contact_popup_auth', data: JSON.stringify( { code } ), nonce: WPFormsConstantContactV3AuthVars.nonce, } ) .done( ( response ) => { if ( ! response.success ) { modal.close(); const errorMessage = '

    ' + WPFormsConstantContactV3AuthVars.strings.error + '

    ' + wpf.sanitizeHTML( response.data ) + '

    '; app.errorModal( errorMessage ); return; } if ( typeof WPFormsBuilder === 'undefined' ) { modal.close(); window.location.href = WPFormsConstantContactV3AuthVars.page_url; return; } WPFormsBuilder.formSave( false ).done( () => { WPFormsBuilder.setCloseConfirmation( false ); WPFormsBuilder.showLoadingOverlay(); location.reload(); } ); } ); }, /** * Show a waiting modal. * * @since 1.9.3 * * @return {Object} Modal object. */ waitModal: () => { return $.alert( { title: '', content: WPFormsConstantContactV3AuthVars.strings.wait, icon: 'fa fa-info-circle', type: 'blue', buttons: false, } ); }, /** * Show an error modal. * * @since 1.9.3 * * @param {string} content Alert text. * * @return {Object} Modal object. */ errorModal: ( content ) => { const strings = window?.wpforms_builder || window?.wpforms_admin; return $.alert( { title: strings.uh_oh, content, icon: 'fa fa-exclamation-circle', type: 'red', buttons: { cancel: { text: strings.cancel, action: () => { app.isOpened = false; }, }, }, } ); }, /** * Prompt and start migration from v2 to v3 in the notice. * * @since 1.9.3 * * @param {Object} e Event object. */ promptMigration( e ) { e.preventDefault(); const modal = app.waitModal(); $.post( { url: WPFormsConstantContactV3AuthVars.ajax_url, data: { action: 'wpforms_constant_contact_migration_prompt', nonce: WPFormsConstantContactV3AuthVars.nonce, }, success: () => { modal.close(); window.location.href = WPFormsConstantContactV3AuthVars.page_url; }, error: () => { modal.close(); app.errorModal( WPFormsConstantContactV3AuthVars.strings.error ); }, } ); }, }; // Provide access to public functions/properties. return app; }( document, window, jQuery ) ); // Initialize. WPFormsConstantContactV3Auth.init(); integrations/constant-contact-v3/auth.min.js000064400000004710151716470030015151 0ustar00const WPFormsConstantContactV3Auth=window.WPFormsConstantContactV3Auth||function(n,e,c){const i={isOpened:!1,listenURL:"",init:()=>{c(i.ready)},ready:()=>{var t=new URL(WPFormsConstantContactV3AuthVars.auth_url).searchParams.get("redirect_uri");i.listenURL=new URL(t).origin,c(n).on("click",".wpforms-constant-contact-v3-auth, .wpforms-builder-constant-contact-v3-provider-sign-up",i.showWindow).on("click","#wpforms-settings-constant-contact-v3-migration-prompt-link",i.promptMigration)},showWindow:t=>{if(t.preventDefault(),!i.isOpened){var t=WPFormsConstantContactV3AuthVars.auth_url,n=screen.width/2-250,o=screen.height/2-300,a=c(".wpforms-constant-contact-v3-auth").data("login-hint"),t=new URL(t);a&&t.searchParams.set("login_hint",a);const r=e.open(t.toString(),"authPopup","width=500, height=600, top="+o+", left="+n),s=(e.addEventListener("message",i.listenResponse),setInterval(()=>{r.closed&&(clearInterval(s),i.isOpened=!1)},1e3));i.isOpened=!0}},listenResponse:t=>{t.origin===i.listenURL&&(t.data?i.saveAccount(t.data):i.errorModal(WPFormsConstantContactV3AuthVars.strings.error))},saveAccount:t=>{const n=i.waitModal();c.post(WPFormsConstantContactV3AuthVars.ajax_url,{action:"wpforms_constant_contact_popup_auth",data:JSON.stringify({code:t}),nonce:WPFormsConstantContactV3AuthVars.nonce}).done(t=>{t.success?"undefined"==typeof WPFormsBuilder?(n.close(),e.location.href=WPFormsConstantContactV3AuthVars.page_url):WPFormsBuilder.formSave(!1).done(()=>{WPFormsBuilder.setCloseConfirmation(!1),WPFormsBuilder.showLoadingOverlay(),location.reload()}):(n.close(),t="

    "+WPFormsConstantContactV3AuthVars.strings.error+"

    "+wpf.sanitizeHTML(t.data)+"

    ",i.errorModal(t))})},waitModal:()=>c.alert({title:"",content:WPFormsConstantContactV3AuthVars.strings.wait,icon:"fa fa-info-circle",type:"blue",buttons:!1}),errorModal:t=>{var n=e?.wpforms_builder||e?.wpforms_admin;return c.alert({title:n.uh_oh,content:t,icon:"fa fa-exclamation-circle",type:"red",buttons:{cancel:{text:n.cancel,action:()=>{i.isOpened=!1}}}})},promptMigration(t){t.preventDefault();const n=i.waitModal();c.post({url:WPFormsConstantContactV3AuthVars.ajax_url,data:{action:"wpforms_constant_contact_migration_prompt",nonce:WPFormsConstantContactV3AuthVars.nonce},success:()=>{n.close(),e.location.href=WPFormsConstantContactV3AuthVars.page_url},error:()=>{n.close(),i.errorModal(WPFormsConstantContactV3AuthVars.strings.error)}})}};return i}(document,window,jQuery);WPFormsConstantContactV3Auth.init();integrations/constant-contact-v3/builder.js000064400000035641151716470030015063 0ustar00/* global WPForms, wpf */ /** * WPForms Providers Builder ConstantContactV3 module. * * @since 1.9.3 */ WPForms.Admin.Builder.Providers.ConstantContactV3 = WPForms.Admin.Builder.Providers.ConstantContactV3 || ( function( document, window, $ ) { /** * Public functions and properties. * * @since 1.9.3 * * @type {Object} */ const app = { /** * CSS selectors. * * @since 1.9.3 * * @type {Object} */ selectors: { accountField: '.js-wpforms-builder-constant-contact-v3-provider-connection-account', actionData: '.wpforms-builder-constant-contact-v3-provider-actions-data', actionField: '.js-wpforms-builder-constant-contact-v3-provider-connection-action', connection: '.wpforms-panel-content-section-constant-contact-v3 .wpforms-builder-provider-connection', }, /** * jQuery elements. * * @since 1.9.3 * * @type {Object} */ $elements: { $connections: $( '.wpforms-panel-content-section-constant-contact-v3 .wpforms-builder-provider-connections' ), $holder: $( '#wpforms-panel-providers' ), $panel: $( '#constant-contact-v3-provider' ), }, /** * Current provider slug. * * @since 1.9.3 * * @type {string} */ provider: 'constant-contact-v3', /** * This is a shortcut to the WPForms.Admin.Builder.Providers object, * that handles the parent all-providers functionality. * * @since 1.9.3 * * @type {Object} */ Providers: {}, /** * This is a shortcut to the WPForms.Admin.Builder.Templates object, * that handles all the template management. * * @since 1.9.3 * * @type {Object} */ Templates: {}, /** * This is a shortcut to the WPForms.Admin.Builder.Providers.cache object, * that handles all the cache management. * * @since 1.9.3 * * @type {Object} */ Cache: {}, /** * This is a flag for ready state. * * @since 1.9.3 * * @type {boolean} */ isReady: false, /** * Start the engine. * * Run initialization on the providers panel only. * * @since 1.9.3 */ init() { // We are requesting/loading a Providers panel. if ( wpf.getQueryString( 'view' ) === 'providers' ) { app.$elements.$holder.on( 'WPForms.Admin.Builder.Providers.ready', app.ready ); } // We have switched to a Providers panel. $( document ).on( 'wpformsPanelSwitched', function( event, panel ) { if ( panel === 'providers' ) { app.ready(); } } ); }, /** * Initialized once the DOM and Providers are fully loaded. * * @since 1.9.3 */ ready() { if ( app.isReady ) { return; } app.Providers = WPForms.Admin.Builder.Providers; app.Templates = WPForms.Admin.Builder.Templates; app.Cache = app.Providers.cache; // Register custom Underscore.js templates. app.Templates.add( [ 'wpforms-constant-contact-v3-builder-content-connection', 'wpforms-constant-contact-v3-builder-content-connection-error', 'wpforms-constant-contact-v3-builder-content-connection-select-field', 'wpforms-constant-contact-v3-builder-content-connection-conditionals', ] ); // Events registration. app.bindUIActions(); app.bindTriggers(); app.processInitial(); // Save a flag for ready state. app.isReady = true; }, /** * Process various events as a response to UI interactions. * * @since 1.9.3 */ bindUIActions() { app.$elements.$panel .on( 'connectionCreate', app.connection.create ) .on( 'connectionDelete', app.connection.delete ) .on( 'change', app.selectors.accountField, app.ui.accountField.change ) .on( 'change', app.selectors.actionField, app.ui.actionField.change ); }, /** * Fire certain events on certain actions, specific for related connections. * These are not directly caused by user manipulations. * * @since 1.9.3 */ bindTriggers() { app.$elements.$connections.on( 'connectionsDataLoaded', function( event, data ) { if ( _.isEmpty( data.connections ) ) { return; } for ( const connectionId in data.connections ) { app.connection.generate( { connection: data.connections[ connectionId ], conditional: data.conditionals[ connectionId ], } ); } } ); app.$elements.$connections.on( 'connectionGenerated', function( event, data ) { const $connection = app.connection.getById( data.connection.id ); if ( _.has( data.connection, 'isNew' ) && data.connection.isNew ) { // Run replacing temporary connection ID if it's a new connection. app.connection.replaceIds( data.connection.id, $connection ); return; } $( app.selectors.actionField, $connection ).trigger( 'change' ); } ); }, /** * Compile template with data if any and display them on a page. * * @since 1.9.3 */ processInitial() { app.$elements.$connections.prepend( app.tmpl.commonsHTML() ); app.connection.dataLoad(); }, /** * Connection property. * * @since 1.9.3 */ connection: { /** * Sometimes we might need to a get a connection DOM element by its ID. * * @since 1.9.3 * * @param {string} connectionId Connection ID to search for a DOM element by. * * @return {jQuery} jQuery object for connection. */ getById( connectionId ) { return app.$elements.$connections.find( '.wpforms-builder-provider-connection[data-connection_id="' + connectionId + '"]' ); }, /** * Sometimes in DOM we might have placeholders or temporary connection IDs. * We need to replace them with actual values. * * @since 1.9.3 * * @param {string} connectionId New connection ID to replace to. * @param {Object} $connection jQuery DOM connection element. */ replaceIds( connectionId, $connection ) { // Replace old temporary %connection_id% from PHP code with the new one. $connection.find( 'input, select, label' ).each( function() { const $this = $( this ); if ( $this.attr( 'name' ) ) { $this.attr( 'name', $this.attr( 'name' ).replace( /%connection_id%/gi, connectionId ) ); } if ( $this.attr( 'id' ) ) { $this.attr( 'id', $this.attr( 'id' ).replace( /%connection_id%/gi, connectionId ) ); } if ( $this.attr( 'for' ) ) { $this.attr( 'for', $this.attr( 'for' ).replace( /%connection_id%/gi, connectionId ) ); } if ( $this.attr( 'data-name' ) ) { $this.attr( 'data-name', $this.attr( 'data-name' ).replace( /%connection_id%/gi, connectionId ) ); } } ); }, /** * Create a connection using the user entered name. * * @since 1.9.3 * * @param {Object} event Event object. * @param {string} name Connection name. */ create( event, name ) { const connectionId = new Date().getTime().toString( 16 ), connection = { id: connectionId, name, isNew: true, }; app.Cache.addTo( app.provider, 'connections', connectionId, connection ); app.connection.generate( { connection, } ); }, /** * Connection is deleted - delete a cache as well. * * @since 1.9.3 * * @param {Object} event Event object. * @param {Object} $connection jQuery DOM element for a connection. */ delete( event, $connection ) { const $holder = app.Providers.getProviderHolder( app.provider ); if ( ! $connection.closest( $holder ).length ) { return; } const connectionId = $connection.data( 'connection_id' ); if ( _.isString( connectionId ) ) { app.Cache.deleteFrom( app.provider, 'connections', connectionId ); } }, /** * Get the template and data for a connection and process it. * * @since 1.9.3 * * @param {Object} data Connection data. * * @return {void} */ generate( data ) { const accounts = app.Cache.get( app.provider, 'accounts' ); if ( _.isEmpty( accounts ) || ! app.account.isAccountExists( data.connection.account_id, accounts ) ) { return; } const actions = app.Cache.get( app.provider, 'actions' ), lists = app.Cache.get( app.provider, 'lists' ); return app.connection.renderConnections( accounts, lists, actions, data ); }, /** * Render connections. * * @since 1.9.3 * * @param {Object} accounts List of accounts. * @param {Object} lists List of lists. * @param {Object} actions List of actions. * @param {Object} data Connection data. */ renderConnections( accounts, lists, actions, data ) { if ( ! app.account.isAccountExists( data.connection.account_id, accounts ) ) { return; } const tmplConnection = app.Templates.get( 'wpforms-' + app.provider + '-builder-content-connection' ), tmplConditional = app.Templates.get( 'wpforms-constant-contact-v3-builder-content-connection-conditionals' ), conditional = _.has( data.connection, 'isNew' ) && data.connection.isNew ? tmplConditional() : data.conditional; app.$elements.$connections.prepend( tmplConnection( { accounts, lists, actions, connection: data.connection, conditional, provider: app.provider, } ) ); app.$elements.$connections.trigger( 'connectionGenerated', [ data ] ); }, /** * Fire AJAX-request to retrieve the list of all saved connections. * * @since 1.9.3 */ dataLoad() { app .Providers.ajax .request( app.provider, { data: { task: 'connections_get', }, } ) .done( function( response ) { if ( ! response.success || ! _.has( response.data, 'connections' ) ) { return; } [ 'accounts', 'actions', 'actions_fields', 'conditionals', 'connections', 'custom_fields', 'lists', ].forEach( ( dataType ) => { app.Cache.set( app.provider, dataType, jQuery.extend( {}, response.data[ dataType ] ) ); } ); app.$elements.$connections.trigger( 'connectionsDataLoaded', [ response.data ] ); } ); }, }, /** * Account property. * * @since 1.9.3 */ account: { /** * Check if a provided account is listed inside an account list. * * @since 1.9.3 * * @param {string} accountId Connection account ID to check. * @param {Object} accounts Array of objects, usually received from API. * * @return {boolean} True if an account exists. */ isAccountExists( accountId, accounts ) { if ( _.isEmpty( accounts ) ) { return false; } // New connections that have not been saved don't have the account ID yet. if ( _.isEmpty( accountId ) ) { return true; } return _.has( accounts, accountId ); }, }, /** * All methods that modify the UI of a page. * * @since 1.9.3 */ ui: { /** * Account field methods. * * @since 1.9.3 */ accountField: { /** * Callback-function on change event. * * @since 1.9.3 */ change() { const $this = $( this ), $connection = $this.closest( app.selectors.connection ), $actionName = $( app.selectors.actionField, $connection ); $actionName.prop( 'selectedIndex', 0 ).trigger( 'change' ); // If an account is empty. if ( _.isEmpty( $this.val() ) ) { $actionName.prop( 'disabled', true ); $( app.selectors.actionData, $connection ).html( '' ); return; } $actionName.prop( 'disabled', false ); $this.removeClass( 'wpforms-error' ); }, }, /** * Action methods. * * @since 1.9.3 */ actionField: { /** * Callback-function on change event. * * @since 1.9.3 */ change() { const $this = $( this ), $connection = $this.closest( app.selectors.connection ), $account = $( app.selectors.accountField, $connection ), $action = $( app.selectors.actionField, $connection ); app.ui.actionField.render( { action: 'action', target: $this, /* eslint-disable camelcase */ account_id: $account.val(), action_name: $action.val(), connection_id: $connection.data( 'connection_id' ), /* eslint-enable camelcase */ } ); $this.removeClass( 'wpforms-error' ); }, /** * Render HTML. * * @since 1.9.3 * * @param {Object} args Arguments. */ render( args ) { const fields = app.tmpl.renderActionFields( args ), $connection = app.connection.getById( args.connection_id ), $connectionData = $( app.selectors.actionData, $connection ); $connectionData.html( fields ); app.$elements.$holder.trigger( 'connectionRendered', [ app.provider, args.connection_id ] ); }, /** * Get a list of constant-contact lists. * * @since 1.9.3 * * @param {string} accountId Account ID. * * @return {Array} List of constant-contact lists. */ getList( accountId ) { const listsCache = app.Cache.get( app.provider, 'lists' ); return ! _.isEmpty( listsCache ) && ! _.isEmpty( listsCache[ accountId ] ) ? listsCache[ accountId ] : []; }, }, }, /** * All methods for JavaScript templates. * * @since 1.9.3 */ tmpl: { /** * Compile and retrieve an HTML for common elements. * * @since 1.9.3 * * @return {string} Compiled HTML. */ commonsHTML() { const tmplError = app.Templates.get( 'wpforms-' + app.provider + '-builder-content-connection-error' ); return tmplError(); }, /** * Compile and retrieve an HTML for "Custom Fields Table". * * @since 1.9.3 * * @param {Object} args Arguments * * @return {string} Compiled HTML. */ renderActionFields( args ) { const fields = wpf.getFields(), actionsFields = app.Cache.get( app.provider, 'actions_fields' ), customFields = app.Cache.get( app.provider, 'custom_fields' ), connection = app.Cache.getById( app.provider, 'connections', args.connection_id ); let fieldHTML = ''; $.each( actionsFields[ args.target.val() ], function( key, field ) { if ( key === 'custom_fields' ) { const tmplFields = app.Templates.get( 'wpforms-providers-builder-content-connection-fields' ); fieldHTML += tmplFields( { connection, fields, provider: { slug: app.provider, fields: customFields[ args.account_id ], }, isSupportSubfields: true, } ); return; } const options = key === 'list' ? app.ui.actionField.getList( args.account_id ) : Object.values( fields ); const templateName = 'wpforms-' + app.provider + '-builder-content-connection-' + field.type + '-field'; const tmplField = app.Templates.get( templateName ); fieldHTML += tmplField( { connection, name: key, field, provider: { slug: app.provider, fields: actionsFields[ args.target.val() ], }, options, } ); } ); return fieldHTML; }, }, }; // Provide access to public functions/properties. return app; }( document, window, jQuery ) ); // Initialize. WPForms.Admin.Builder.Providers.ConstantContactV3.init(); integrations/constant-contact-v3/builder.min.js000064400000013727151716470030015646 0ustar00WPForms.Admin.Builder.Providers.ConstantContactV3=WPForms.Admin.Builder.Providers.ConstantContactV3||function(n,l){const p={selectors:{accountField:".js-wpforms-builder-constant-contact-v3-provider-connection-account",actionData:".wpforms-builder-constant-contact-v3-provider-actions-data",actionField:".js-wpforms-builder-constant-contact-v3-provider-connection-action",connection:".wpforms-panel-content-section-constant-contact-v3 .wpforms-builder-provider-connection"},$elements:{$connections:l(".wpforms-panel-content-section-constant-contact-v3 .wpforms-builder-provider-connections"),$holder:l("#wpforms-panel-providers"),$panel:l("#constant-contact-v3-provider")},provider:"constant-contact-v3",Providers:{},Templates:{},Cache:{},isReady:!1,init(){"providers"===wpf.getQueryString("view")&&p.$elements.$holder.on("WPForms.Admin.Builder.Providers.ready",p.ready),l(n).on("wpformsPanelSwitched",function(n,e){"providers"===e&&p.ready()})},ready(){p.isReady||(p.Providers=WPForms.Admin.Builder.Providers,p.Templates=WPForms.Admin.Builder.Templates,p.Cache=p.Providers.cache,p.Templates.add(["wpforms-constant-contact-v3-builder-content-connection","wpforms-constant-contact-v3-builder-content-connection-error","wpforms-constant-contact-v3-builder-content-connection-select-field","wpforms-constant-contact-v3-builder-content-connection-conditionals"]),p.bindUIActions(),p.bindTriggers(),p.processInitial(),p.isReady=!0)},bindUIActions(){p.$elements.$panel.on("connectionCreate",p.connection.create).on("connectionDelete",p.connection.delete).on("change",p.selectors.accountField,p.ui.accountField.change).on("change",p.selectors.actionField,p.ui.actionField.change)},bindTriggers(){p.$elements.$connections.on("connectionsDataLoaded",function(n,e){if(!_.isEmpty(e.connections))for(const t in e.connections)p.connection.generate({connection:e.connections[t],conditional:e.conditionals[t]})}),p.$elements.$connections.on("connectionGenerated",function(n,e){var t=p.connection.getById(e.connection.id);_.has(e.connection,"isNew")&&e.connection.isNew?p.connection.replaceIds(e.connection.id,t):l(p.selectors.actionField,t).trigger("change")})},processInitial(){p.$elements.$connections.prepend(p.tmpl.commonsHTML()),p.connection.dataLoad()},connection:{getById(n){return p.$elements.$connections.find('.wpforms-builder-provider-connection[data-connection_id="'+n+'"]')},replaceIds(e,n){n.find("input, select, label").each(function(){var n=l(this);n.attr("name")&&n.attr("name",n.attr("name").replace(/%connection_id%/gi,e)),n.attr("id")&&n.attr("id",n.attr("id").replace(/%connection_id%/gi,e)),n.attr("for")&&n.attr("for",n.attr("for").replace(/%connection_id%/gi,e)),n.attr("data-name")&&n.attr("data-name",n.attr("data-name").replace(/%connection_id%/gi,e))})},create(n,e){var t=(new Date).getTime().toString(16),e={id:t,name:e,isNew:!0};p.Cache.addTo(p.provider,"connections",t,e),p.connection.generate({connection:e})},delete(n,e){var t=p.Providers.getProviderHolder(p.provider);e.closest(t).length&&(t=e.data("connection_id"),_.isString(t))&&p.Cache.deleteFrom(p.provider,"connections",t)},generate(n){var e,t,o=p.Cache.get(p.provider,"accounts");if(!_.isEmpty(o)&&p.account.isAccountExists(n.connection.account_id,o))return e=p.Cache.get(p.provider,"actions"),t=p.Cache.get(p.provider,"lists"),p.connection.renderConnections(o,t,e,n)},renderConnections(n,e,t,o){var c,i;p.account.isAccountExists(o.connection.account_id,n)&&(c=p.Templates.get("wpforms-"+p.provider+"-builder-content-connection"),i=p.Templates.get("wpforms-constant-contact-v3-builder-content-connection-conditionals"),i=_.has(o.connection,"isNew")&&o.connection.isNew?i():o.conditional,p.$elements.$connections.prepend(c({accounts:n,lists:e,actions:t,connection:o.connection,conditional:i,provider:p.provider})),p.$elements.$connections.trigger("connectionGenerated",[o]))},dataLoad(){p.Providers.ajax.request(p.provider,{data:{task:"connections_get"}}).done(function(e){e.success&&_.has(e.data,"connections")&&(["accounts","actions","actions_fields","conditionals","connections","custom_fields","lists"].forEach(n=>{p.Cache.set(p.provider,n,jQuery.extend({},e.data[n]))}),p.$elements.$connections.trigger("connectionsDataLoaded",[e.data]))})}},account:{isAccountExists(n,e){return!_.isEmpty(e)&&(!!_.isEmpty(n)||_.has(e,n))}},ui:{accountField:{change(){var n=l(this),e=n.closest(p.selectors.connection),t=l(p.selectors.actionField,e);t.prop("selectedIndex",0).trigger("change"),_.isEmpty(n.val())?(t.prop("disabled",!0),l(p.selectors.actionData,e).html("")):(t.prop("disabled",!1),n.removeClass("wpforms-error"))}},actionField:{change(){var n=l(this),e=n.closest(p.selectors.connection),t=l(p.selectors.accountField,e),o=l(p.selectors.actionField,e);p.ui.actionField.render({action:"action",target:n,account_id:t.val(),action_name:o.val(),connection_id:e.data("connection_id")}),n.removeClass("wpforms-error")},render(n){var e=p.tmpl.renderActionFields(n),t=p.connection.getById(n.connection_id);l(p.selectors.actionData,t).html(e),p.$elements.$holder.trigger("connectionRendered",[p.provider,n.connection_id])},getList(n){var e=p.Cache.get(p.provider,"lists");return _.isEmpty(e)||_.isEmpty(e[n])?[]:e[n]}}},tmpl:{commonsHTML(){return p.Templates.get("wpforms-"+p.provider+"-builder-content-connection-error")()},renderActionFields(c){const i=wpf.getFields(),r=p.Cache.get(p.provider,"actions_fields"),s=p.Cache.get(p.provider,"custom_fields"),a=p.Cache.getById(p.provider,"connections",c.connection_id);let d="";return l.each(r[c.target.val()],function(n,e){var t,o;"custom_fields"===n?(t=p.Templates.get("wpforms-providers-builder-content-connection-fields"),d+=t({connection:a,fields:i,provider:{slug:p.provider,fields:s[c.account_id]},isSupportSubfields:!0})):(t="list"===n?p.ui.actionField.getList(c.account_id):Object.values(i),o="wpforms-"+p.provider+"-builder-content-connection-"+e.type+"-field",o=p.Templates.get(o),d+=o({connection:a,name:n,field:e,provider:{slug:p.provider,fields:r[c.target.val()]},options:t}))}),d}}};return p}(document,(window,jQuery)),WPForms.Admin.Builder.Providers.ConstantContactV3.init();integrations/divi/formselector.es5.js000064400000512206151716470030013754 0ustar00(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) { var fn = queue.shift(); fn(); } } }, true); return function nextTick(fn) { queue.push(fn); window.postMessage('process-tick', '*'); }; } return function nextTick(fn) { setTimeout(fn, 0); }; })(); process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; function noop() {} process.on = noop; process.addListener = noop; process.once = noop; process.off = noop; process.removeListener = noop; process.removeAllListeners = noop; process.emit = noop; process.binding = function (name) { throw new Error('process.binding is not supported'); } // TODO(shtylman) process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported'); }; },{}],2:[function(require,module,exports){ /* object-assign (c) Sindre Sorhus @license MIT */ 'use strict'; /* eslint-disable no-unused-vars */ var getOwnPropertySymbols = Object.getOwnPropertySymbols; var hasOwnProperty = Object.prototype.hasOwnProperty; var propIsEnumerable = Object.prototype.propertyIsEnumerable; function toObject(val) { if (val === null || val === undefined) { throw new TypeError('Object.assign cannot be called with null or undefined'); } return Object(val); } function shouldUseNative() { try { if (!Object.assign) { return false; } // Detect buggy property enumeration order in older V8 versions. // https://bugs.chromium.org/p/v8/issues/detail?id=4118 var test1 = new String('abc'); // eslint-disable-line no-new-wrappers test1[5] = 'de'; if (Object.getOwnPropertyNames(test1)[0] === '5') { return false; } // https://bugs.chromium.org/p/v8/issues/detail?id=3056 var test2 = {}; for (var i = 0; i < 10; i++) { test2['_' + String.fromCharCode(i)] = i; } var order2 = Object.getOwnPropertyNames(test2).map(function (n) { return test2[n]; }); if (order2.join('') !== '0123456789') { return false; } // https://bugs.chromium.org/p/v8/issues/detail?id=3056 var test3 = {}; 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { test3[letter] = letter; }); if (Object.keys(Object.assign({}, test3)).join('') !== 'abcdefghijklmnopqrst') { return false; } return true; } catch (err) { // We don't expect any of the above to throw, but better to be safe. return false; } } module.exports = shouldUseNative() ? Object.assign : function (target, source) { var from; var to = toObject(target); var symbols; for (var s = 1; s < arguments.length; s++) { from = Object(arguments[s]); for (var key in from) { if (hasOwnProperty.call(from, key)) { to[key] = from[key]; } } if (getOwnPropertySymbols) { symbols = getOwnPropertySymbols(from); for (var i = 0; i < symbols.length; i++) { if (propIsEnumerable.call(from, symbols[i])) { to[symbols[i]] = from[symbols[i]]; } } } } return to; }; },{}],3:[function(require,module,exports){ (function (process){ /** * Copyright (c) 2013-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; var printWarning = function() {}; if (process.env.NODE_ENV !== 'production') { var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret'); var loggedTypeFailures = {}; var has = require('./lib/has'); printWarning = function(text) { var message = 'Warning: ' + text; if (typeof console !== 'undefined') { console.error(message); } try { // --- Welcome to debugging React --- // This error was thrown as a convenience so that you can use this stack // to find the callsite that caused this warning to fire. throw new Error(message); } catch (x) { /**/ } }; } /** * Assert that the values match with the type specs. * Error messages are memorized and will only be shown once. * * @param {object} typeSpecs Map of name to a ReactPropType * @param {object} values Runtime values that need to be type-checked * @param {string} location e.g. "prop", "context", "child context" * @param {string} componentName Name of the component for error messages. * @param {?Function} getStack Returns the component stack. * @private */ function checkPropTypes(typeSpecs, values, location, componentName, getStack) { if (process.env.NODE_ENV !== 'production') { for (var typeSpecName in typeSpecs) { if (has(typeSpecs, typeSpecName)) { var error; // Prop type validation may throw. In case they do, we don't want to // fail the render phase where it didn't fail before. So we log it. // After these have been cleaned up, we'll let them throw. try { // This is intentionally an invariant that gets caught. It's the same // behavior as without this statement except with a better message. if (typeof typeSpecs[typeSpecName] !== 'function') { var err = Error( (componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' + 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' + 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.' ); err.name = 'Invariant Violation'; throw err; } error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret); } catch (ex) { error = ex; } if (error && !(error instanceof Error)) { printWarning( (componentName || 'React class') + ': type specification of ' + location + ' `' + typeSpecName + '` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a ' + typeof error + '. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).' ); } if (error instanceof Error && !(error.message in loggedTypeFailures)) { // Only monitor this failure once because there tends to be a lot of the // same error. loggedTypeFailures[error.message] = true; var stack = getStack ? getStack() : ''; printWarning( 'Failed ' + location + ' type: ' + error.message + (stack != null ? stack : '') ); } } } } } /** * Resets warning cache when testing. * * @private */ checkPropTypes.resetWarningCache = function() { if (process.env.NODE_ENV !== 'production') { loggedTypeFailures = {}; } } module.exports = checkPropTypes; }).call(this,require("hmr7eR")) },{"./lib/ReactPropTypesSecret":7,"./lib/has":8,"hmr7eR":1}],4:[function(require,module,exports){ /** * Copyright (c) 2013-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret'); function emptyFunction() {} function emptyFunctionWithReset() {} emptyFunctionWithReset.resetWarningCache = emptyFunction; module.exports = function() { function shim(props, propName, componentName, location, propFullName, secret) { if (secret === ReactPropTypesSecret) { // It is still safe when called from React. return; } var err = new Error( 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' + 'Use PropTypes.checkPropTypes() to call them. ' + 'Read more at http://fb.me/use-check-prop-types' ); err.name = 'Invariant Violation'; throw err; }; shim.isRequired = shim; function getShim() { return shim; }; // Important! // Keep this list in sync with production version in `./factoryWithTypeCheckers.js`. var ReactPropTypes = { array: shim, bigint: shim, bool: shim, func: shim, number: shim, object: shim, string: shim, symbol: shim, any: shim, arrayOf: getShim, element: shim, elementType: shim, instanceOf: getShim, node: shim, objectOf: getShim, oneOf: getShim, oneOfType: getShim, shape: getShim, exact: getShim, checkPropTypes: emptyFunctionWithReset, resetWarningCache: emptyFunction }; ReactPropTypes.PropTypes = ReactPropTypes; return ReactPropTypes; }; },{"./lib/ReactPropTypesSecret":7}],5:[function(require,module,exports){ (function (process){ /** * Copyright (c) 2013-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; var ReactIs = require('react-is'); var assign = require('object-assign'); var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret'); var has = require('./lib/has'); var checkPropTypes = require('./checkPropTypes'); var printWarning = function() {}; if (process.env.NODE_ENV !== 'production') { printWarning = function(text) { var message = 'Warning: ' + text; if (typeof console !== 'undefined') { console.error(message); } try { // --- Welcome to debugging React --- // This error was thrown as a convenience so that you can use this stack // to find the callsite that caused this warning to fire. throw new Error(message); } catch (x) {} }; } function emptyFunctionThatReturnsNull() { return null; } module.exports = function(isValidElement, throwOnDirectAccess) { /* global Symbol */ var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator; var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec. /** * Returns the iterator method function contained on the iterable object. * * Be sure to invoke the function with the iterable as context: * * var iteratorFn = getIteratorFn(myIterable); * if (iteratorFn) { * var iterator = iteratorFn.call(myIterable); * ... * } * * @param {?object} maybeIterable * @return {?function} */ function getIteratorFn(maybeIterable) { var iteratorFn = maybeIterable && (ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]); if (typeof iteratorFn === 'function') { return iteratorFn; } } /** * Collection of methods that allow declaration and validation of props that are * supplied to React components. Example usage: * * var Props = require('ReactPropTypes'); * var MyArticle = React.createClass({ * propTypes: { * // An optional string prop named "description". * description: Props.string, * * // A required enum prop named "category". * category: Props.oneOf(['News','Photos']).isRequired, * * // A prop named "dialog" that requires an instance of Dialog. * dialog: Props.instanceOf(Dialog).isRequired * }, * render: function() { ... } * }); * * A more formal specification of how these methods are used: * * type := array|bool|func|object|number|string|oneOf([...])|instanceOf(...) * decl := ReactPropTypes.{type}(.isRequired)? * * Each and every declaration produces a function with the same signature. This * allows the creation of custom validation functions. For example: * * var MyLink = React.createClass({ * propTypes: { * // An optional string or URI prop named "href". * href: function(props, propName, componentName) { * var propValue = props[propName]; * if (propValue != null && typeof propValue !== 'string' && * !(propValue instanceof URI)) { * return new Error( * 'Expected a string or an URI for ' + propName + ' in ' + * componentName * ); * } * } * }, * render: function() {...} * }); * * @internal */ var ANONYMOUS = '<>'; // Important! // Keep this list in sync with production version in `./factoryWithThrowingShims.js`. var ReactPropTypes = { array: createPrimitiveTypeChecker('array'), bigint: createPrimitiveTypeChecker('bigint'), bool: createPrimitiveTypeChecker('boolean'), func: createPrimitiveTypeChecker('function'), number: createPrimitiveTypeChecker('number'), object: createPrimitiveTypeChecker('object'), string: createPrimitiveTypeChecker('string'), symbol: createPrimitiveTypeChecker('symbol'), any: createAnyTypeChecker(), arrayOf: createArrayOfTypeChecker, element: createElementTypeChecker(), elementType: createElementTypeTypeChecker(), instanceOf: createInstanceTypeChecker, node: createNodeChecker(), objectOf: createObjectOfTypeChecker, oneOf: createEnumTypeChecker, oneOfType: createUnionTypeChecker, shape: createShapeTypeChecker, exact: createStrictShapeTypeChecker, }; /** * inlined Object.is polyfill to avoid requiring consumers ship their own * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is */ /*eslint-disable no-self-compare*/ function is(x, y) { // SameValue algorithm if (x === y) { // Steps 1-5, 7-10 // Steps 6.b-6.e: +0 != -0 return x !== 0 || 1 / x === 1 / y; } else { // Step 6.a: NaN == NaN return x !== x && y !== y; } } /*eslint-enable no-self-compare*/ /** * We use an Error-like object for backward compatibility as people may call * PropTypes directly and inspect their output. However, we don't use real * Errors anymore. We don't inspect their stack anyway, and creating them * is prohibitively expensive if they are created too often, such as what * happens in oneOfType() for any type before the one that matched. */ function PropTypeError(message, data) { this.message = message; this.data = data && typeof data === 'object' ? data: {}; this.stack = ''; } // Make `instanceof Error` still work for returned errors. PropTypeError.prototype = Error.prototype; function createChainableTypeChecker(validate) { if (process.env.NODE_ENV !== 'production') { var manualPropTypeCallCache = {}; var manualPropTypeWarningCount = 0; } function checkType(isRequired, props, propName, componentName, location, propFullName, secret) { componentName = componentName || ANONYMOUS; propFullName = propFullName || propName; if (secret !== ReactPropTypesSecret) { if (throwOnDirectAccess) { // New behavior only for users of `prop-types` package var err = new Error( 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' + 'Use `PropTypes.checkPropTypes()` to call them. ' + 'Read more at http://fb.me/use-check-prop-types' ); err.name = 'Invariant Violation'; throw err; } else if (process.env.NODE_ENV !== 'production' && typeof console !== 'undefined') { // Old behavior for people using React.PropTypes var cacheKey = componentName + ':' + propName; if ( !manualPropTypeCallCache[cacheKey] && // Avoid spamming the console because they are often not actionable except for lib authors manualPropTypeWarningCount < 3 ) { printWarning( 'You are manually calling a React.PropTypes validation ' + 'function for the `' + propFullName + '` prop on `' + componentName + '`. This is deprecated ' + 'and will throw in the standalone `prop-types` package. ' + 'You may be seeing this warning due to a third-party PropTypes ' + 'library. See https://fb.me/react-warning-dont-call-proptypes ' + 'for details.' ); manualPropTypeCallCache[cacheKey] = true; manualPropTypeWarningCount++; } } } if (props[propName] == null) { if (isRequired) { if (props[propName] === null) { return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required ' + ('in `' + componentName + '`, but its value is `null`.')); } return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required in ' + ('`' + componentName + '`, but its value is `undefined`.')); } return null; } else { return validate(props, propName, componentName, location, propFullName); } } var chainedCheckType = checkType.bind(null, false); chainedCheckType.isRequired = checkType.bind(null, true); return chainedCheckType; } function createPrimitiveTypeChecker(expectedType) { function validate(props, propName, componentName, location, propFullName, secret) { var propValue = props[propName]; var propType = getPropType(propValue); if (propType !== expectedType) { // `propValue` being instance of, say, date/regexp, pass the 'object' // check, but we can offer a more precise error message here rather than // 'of type `object`'. var preciseType = getPreciseType(propValue); return new PropTypeError( 'Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + preciseType + '` supplied to `' + componentName + '`, expected ') + ('`' + expectedType + '`.'), {expectedType: expectedType} ); } return null; } return createChainableTypeChecker(validate); } function createAnyTypeChecker() { return createChainableTypeChecker(emptyFunctionThatReturnsNull); } function createArrayOfTypeChecker(typeChecker) { function validate(props, propName, componentName, location, propFullName) { if (typeof typeChecker !== 'function') { return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside arrayOf.'); } var propValue = props[propName]; if (!Array.isArray(propValue)) { var propType = getPropType(propValue); return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an array.')); } for (var i = 0; i < propValue.length; i++) { var error = typeChecker(propValue, i, componentName, location, propFullName + '[' + i + ']', ReactPropTypesSecret); if (error instanceof Error) { return error; } } return null; } return createChainableTypeChecker(validate); } function createElementTypeChecker() { function validate(props, propName, componentName, location, propFullName) { var propValue = props[propName]; if (!isValidElement(propValue)) { var propType = getPropType(propValue); return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement.')); } return null; } return createChainableTypeChecker(validate); } function createElementTypeTypeChecker() { function validate(props, propName, componentName, location, propFullName) { var propValue = props[propName]; if (!ReactIs.isValidElementType(propValue)) { var propType = getPropType(propValue); return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement type.')); } return null; } return createChainableTypeChecker(validate); } function createInstanceTypeChecker(expectedClass) { function validate(props, propName, componentName, location, propFullName) { if (!(props[propName] instanceof expectedClass)) { var expectedClassName = expectedClass.name || ANONYMOUS; var actualClassName = getClassName(props[propName]); return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + actualClassName + '` supplied to `' + componentName + '`, expected ') + ('instance of `' + expectedClassName + '`.')); } return null; } return createChainableTypeChecker(validate); } function createEnumTypeChecker(expectedValues) { if (!Array.isArray(expectedValues)) { if (process.env.NODE_ENV !== 'production') { if (arguments.length > 1) { printWarning( 'Invalid arguments supplied to oneOf, expected an array, got ' + arguments.length + ' arguments. ' + 'A common mistake is to write oneOf(x, y, z) instead of oneOf([x, y, z]).' ); } else { printWarning('Invalid argument supplied to oneOf, expected an array.'); } } return emptyFunctionThatReturnsNull; } function validate(props, propName, componentName, location, propFullName) { var propValue = props[propName]; for (var i = 0; i < expectedValues.length; i++) { if (is(propValue, expectedValues[i])) { return null; } } var valuesString = JSON.stringify(expectedValues, function replacer(key, value) { var type = getPreciseType(value); if (type === 'symbol') { return String(value); } return value; }); return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of value `' + String(propValue) + '` ' + ('supplied to `' + componentName + '`, expected one of ' + valuesString + '.')); } return createChainableTypeChecker(validate); } function createObjectOfTypeChecker(typeChecker) { function validate(props, propName, componentName, location, propFullName) { if (typeof typeChecker !== 'function') { return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside objectOf.'); } var propValue = props[propName]; var propType = getPropType(propValue); if (propType !== 'object') { return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an object.')); } for (var key in propValue) { if (has(propValue, key)) { var error = typeChecker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret); if (error instanceof Error) { return error; } } } return null; } return createChainableTypeChecker(validate); } function createUnionTypeChecker(arrayOfTypeCheckers) { if (!Array.isArray(arrayOfTypeCheckers)) { process.env.NODE_ENV !== 'production' ? printWarning('Invalid argument supplied to oneOfType, expected an instance of array.') : void 0; return emptyFunctionThatReturnsNull; } for (var i = 0; i < arrayOfTypeCheckers.length; i++) { var checker = arrayOfTypeCheckers[i]; if (typeof checker !== 'function') { printWarning( 'Invalid argument supplied to oneOfType. Expected an array of check functions, but ' + 'received ' + getPostfixForTypeWarning(checker) + ' at index ' + i + '.' ); return emptyFunctionThatReturnsNull; } } function validate(props, propName, componentName, location, propFullName) { var expectedTypes = []; for (var i = 0; i < arrayOfTypeCheckers.length; i++) { var checker = arrayOfTypeCheckers[i]; var checkerResult = checker(props, propName, componentName, location, propFullName, ReactPropTypesSecret); if (checkerResult == null) { return null; } if (checkerResult.data && has(checkerResult.data, 'expectedType')) { expectedTypes.push(checkerResult.data.expectedType); } } var expectedTypesMessage = (expectedTypes.length > 0) ? ', expected one of type [' + expectedTypes.join(', ') + ']': ''; return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`' + expectedTypesMessage + '.')); } return createChainableTypeChecker(validate); } function createNodeChecker() { function validate(props, propName, componentName, location, propFullName) { if (!isNode(props[propName])) { return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`, expected a ReactNode.')); } return null; } return createChainableTypeChecker(validate); } function invalidValidatorError(componentName, location, propFullName, key, type) { return new PropTypeError( (componentName || 'React class') + ': ' + location + ' type `' + propFullName + '.' + key + '` is invalid; ' + 'it must be a function, usually from the `prop-types` package, but received `' + type + '`.' ); } function createShapeTypeChecker(shapeTypes) { function validate(props, propName, componentName, location, propFullName) { var propValue = props[propName]; var propType = getPropType(propValue); if (propType !== 'object') { return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.')); } for (var key in shapeTypes) { var checker = shapeTypes[key]; if (typeof checker !== 'function') { return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker)); } var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret); if (error) { return error; } } return null; } return createChainableTypeChecker(validate); } function createStrictShapeTypeChecker(shapeTypes) { function validate(props, propName, componentName, location, propFullName) { var propValue = props[propName]; var propType = getPropType(propValue); if (propType !== 'object') { return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.')); } // We need to check all keys in case some are required but missing from props. var allKeys = assign({}, props[propName], shapeTypes); for (var key in allKeys) { var checker = shapeTypes[key]; if (has(shapeTypes, key) && typeof checker !== 'function') { return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker)); } if (!checker) { return new PropTypeError( 'Invalid ' + location + ' `' + propFullName + '` key `' + key + '` supplied to `' + componentName + '`.' + '\nBad object: ' + JSON.stringify(props[propName], null, ' ') + '\nValid keys: ' + JSON.stringify(Object.keys(shapeTypes), null, ' ') ); } var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret); if (error) { return error; } } return null; } return createChainableTypeChecker(validate); } function isNode(propValue) { switch (typeof propValue) { case 'number': case 'string': case 'undefined': return true; case 'boolean': return !propValue; case 'object': if (Array.isArray(propValue)) { return propValue.every(isNode); } if (propValue === null || isValidElement(propValue)) { return true; } var iteratorFn = getIteratorFn(propValue); if (iteratorFn) { var iterator = iteratorFn.call(propValue); var step; if (iteratorFn !== propValue.entries) { while (!(step = iterator.next()).done) { if (!isNode(step.value)) { return false; } } } else { // Iterator will provide entry [k,v] tuples rather than values. while (!(step = iterator.next()).done) { var entry = step.value; if (entry) { if (!isNode(entry[1])) { return false; } } } } } else { return false; } return true; default: return false; } } function isSymbol(propType, propValue) { // Native Symbol. if (propType === 'symbol') { return true; } // falsy value can't be a Symbol if (!propValue) { return false; } // 19.4.3.5 Symbol.prototype[@@toStringTag] === 'Symbol' if (propValue['@@toStringTag'] === 'Symbol') { return true; } // Fallback for non-spec compliant Symbols which are polyfilled. if (typeof Symbol === 'function' && propValue instanceof Symbol) { return true; } return false; } // Equivalent of `typeof` but with special handling for array and regexp. function getPropType(propValue) { var propType = typeof propValue; if (Array.isArray(propValue)) { return 'array'; } if (propValue instanceof RegExp) { // Old webkits (at least until Android 4.0) return 'function' rather than // 'object' for typeof a RegExp. We'll normalize this here so that /bla/ // passes PropTypes.object. return 'object'; } if (isSymbol(propType, propValue)) { return 'symbol'; } return propType; } // This handles more types than `getPropType`. Only used for error messages. // See `createPrimitiveTypeChecker`. function getPreciseType(propValue) { if (typeof propValue === 'undefined' || propValue === null) { return '' + propValue; } var propType = getPropType(propValue); if (propType === 'object') { if (propValue instanceof Date) { return 'date'; } else if (propValue instanceof RegExp) { return 'regexp'; } } return propType; } // Returns a string that is postfixed to a warning about an invalid type. // For example, "undefined" or "of type array" function getPostfixForTypeWarning(value) { var type = getPreciseType(value); switch (type) { case 'array': case 'object': return 'an ' + type; case 'boolean': case 'date': case 'regexp': return 'a ' + type; default: return type; } } // Returns class name of the object, if any. function getClassName(propValue) { if (!propValue.constructor || !propValue.constructor.name) { return ANONYMOUS; } return propValue.constructor.name; } ReactPropTypes.checkPropTypes = checkPropTypes; ReactPropTypes.resetWarningCache = checkPropTypes.resetWarningCache; ReactPropTypes.PropTypes = ReactPropTypes; return ReactPropTypes; }; }).call(this,require("hmr7eR")) },{"./checkPropTypes":3,"./lib/ReactPropTypesSecret":7,"./lib/has":8,"hmr7eR":1,"object-assign":2,"react-is":11}],6:[function(require,module,exports){ (function (process){ /** * Copyright (c) 2013-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ if (process.env.NODE_ENV !== 'production') { var ReactIs = require('react-is'); // By explicitly using `prop-types` you are opting into new development behavior. // http://fb.me/prop-types-in-prod var throwOnDirectAccess = true; module.exports = require('./factoryWithTypeCheckers')(ReactIs.isElement, throwOnDirectAccess); } else { // By explicitly using `prop-types` you are opting into new production behavior. // http://fb.me/prop-types-in-prod module.exports = require('./factoryWithThrowingShims')(); } }).call(this,require("hmr7eR")) },{"./factoryWithThrowingShims":4,"./factoryWithTypeCheckers":5,"hmr7eR":1,"react-is":11}],7:[function(require,module,exports){ /** * Copyright (c) 2013-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; var ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'; module.exports = ReactPropTypesSecret; },{}],8:[function(require,module,exports){ module.exports = Function.call.bind(Object.prototype.hasOwnProperty); },{}],9:[function(require,module,exports){ (function (process){ /** @license React v16.13.1 * react-is.development.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; if (process.env.NODE_ENV !== "production") { (function() { 'use strict'; // The Symbol used to tag the ReactElement-like types. If there is no native Symbol // nor polyfill, then a plain number is used for performance. var hasSymbol = typeof Symbol === 'function' && Symbol.for; var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7; var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca; var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb; var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc; var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2; var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd; var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace; // TODO: We don't use AsyncMode or ConcurrentMode anymore. They were temporary // (unstable) APIs that have been removed. Can we remove the symbols? var REACT_ASYNC_MODE_TYPE = hasSymbol ? Symbol.for('react.async_mode') : 0xeacf; var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf; var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0; var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1; var REACT_SUSPENSE_LIST_TYPE = hasSymbol ? Symbol.for('react.suspense_list') : 0xead8; var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3; var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4; var REACT_BLOCK_TYPE = hasSymbol ? Symbol.for('react.block') : 0xead9; var REACT_FUNDAMENTAL_TYPE = hasSymbol ? Symbol.for('react.fundamental') : 0xead5; var REACT_RESPONDER_TYPE = hasSymbol ? Symbol.for('react.responder') : 0xead6; var REACT_SCOPE_TYPE = hasSymbol ? Symbol.for('react.scope') : 0xead7; function isValidElementType(type) { return typeof type === 'string' || typeof type === 'function' || // Note: its typeof might be other than 'symbol' or 'number' if it's a polyfill. type === REACT_FRAGMENT_TYPE || type === REACT_CONCURRENT_MODE_TYPE || type === REACT_PROFILER_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || typeof type === 'object' && type !== null && (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || type.$$typeof === REACT_FUNDAMENTAL_TYPE || type.$$typeof === REACT_RESPONDER_TYPE || type.$$typeof === REACT_SCOPE_TYPE || type.$$typeof === REACT_BLOCK_TYPE); } function typeOf(object) { if (typeof object === 'object' && object !== null) { var $$typeof = object.$$typeof; switch ($$typeof) { case REACT_ELEMENT_TYPE: var type = object.type; switch (type) { case REACT_ASYNC_MODE_TYPE: case REACT_CONCURRENT_MODE_TYPE: case REACT_FRAGMENT_TYPE: case REACT_PROFILER_TYPE: case REACT_STRICT_MODE_TYPE: case REACT_SUSPENSE_TYPE: return type; default: var $$typeofType = type && type.$$typeof; switch ($$typeofType) { case REACT_CONTEXT_TYPE: case REACT_FORWARD_REF_TYPE: case REACT_LAZY_TYPE: case REACT_MEMO_TYPE: case REACT_PROVIDER_TYPE: return $$typeofType; default: return $$typeof; } } case REACT_PORTAL_TYPE: return $$typeof; } } return undefined; } // AsyncMode is deprecated along with isAsyncMode var AsyncMode = REACT_ASYNC_MODE_TYPE; var ConcurrentMode = REACT_CONCURRENT_MODE_TYPE; var ContextConsumer = REACT_CONTEXT_TYPE; var ContextProvider = REACT_PROVIDER_TYPE; var Element = REACT_ELEMENT_TYPE; var ForwardRef = REACT_FORWARD_REF_TYPE; var Fragment = REACT_FRAGMENT_TYPE; var Lazy = REACT_LAZY_TYPE; var Memo = REACT_MEMO_TYPE; var Portal = REACT_PORTAL_TYPE; var Profiler = REACT_PROFILER_TYPE; var StrictMode = REACT_STRICT_MODE_TYPE; var Suspense = REACT_SUSPENSE_TYPE; var hasWarnedAboutDeprecatedIsAsyncMode = false; // AsyncMode should be deprecated function isAsyncMode(object) { { if (!hasWarnedAboutDeprecatedIsAsyncMode) { hasWarnedAboutDeprecatedIsAsyncMode = true; // Using console['warn'] to evade Babel and ESLint console['warn']('The ReactIs.isAsyncMode() alias has been deprecated, ' + 'and will be removed in React 17+. Update your code to use ' + 'ReactIs.isConcurrentMode() instead. It has the exact same API.'); } } return isConcurrentMode(object) || typeOf(object) === REACT_ASYNC_MODE_TYPE; } function isConcurrentMode(object) { return typeOf(object) === REACT_CONCURRENT_MODE_TYPE; } function isContextConsumer(object) { return typeOf(object) === REACT_CONTEXT_TYPE; } function isContextProvider(object) { return typeOf(object) === REACT_PROVIDER_TYPE; } function isElement(object) { return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE; } function isForwardRef(object) { return typeOf(object) === REACT_FORWARD_REF_TYPE; } function isFragment(object) { return typeOf(object) === REACT_FRAGMENT_TYPE; } function isLazy(object) { return typeOf(object) === REACT_LAZY_TYPE; } function isMemo(object) { return typeOf(object) === REACT_MEMO_TYPE; } function isPortal(object) { return typeOf(object) === REACT_PORTAL_TYPE; } function isProfiler(object) { return typeOf(object) === REACT_PROFILER_TYPE; } function isStrictMode(object) { return typeOf(object) === REACT_STRICT_MODE_TYPE; } function isSuspense(object) { return typeOf(object) === REACT_SUSPENSE_TYPE; } exports.AsyncMode = AsyncMode; exports.ConcurrentMode = ConcurrentMode; exports.ContextConsumer = ContextConsumer; exports.ContextProvider = ContextProvider; exports.Element = Element; exports.ForwardRef = ForwardRef; exports.Fragment = Fragment; exports.Lazy = Lazy; exports.Memo = Memo; exports.Portal = Portal; exports.Profiler = Profiler; exports.StrictMode = StrictMode; exports.Suspense = Suspense; exports.isAsyncMode = isAsyncMode; exports.isConcurrentMode = isConcurrentMode; exports.isContextConsumer = isContextConsumer; exports.isContextProvider = isContextProvider; exports.isElement = isElement; exports.isForwardRef = isForwardRef; exports.isFragment = isFragment; exports.isLazy = isLazy; exports.isMemo = isMemo; exports.isPortal = isPortal; exports.isProfiler = isProfiler; exports.isStrictMode = isStrictMode; exports.isSuspense = isSuspense; exports.isValidElementType = isValidElementType; exports.typeOf = typeOf; })(); } }).call(this,require("hmr7eR")) },{"hmr7eR":1}],10:[function(require,module,exports){ /** @license React v16.13.1 * react-is.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 'use strict';var b="function"===typeof Symbol&&Symbol.for,c=b?Symbol.for("react.element"):60103,d=b?Symbol.for("react.portal"):60106,e=b?Symbol.for("react.fragment"):60107,f=b?Symbol.for("react.strict_mode"):60108,g=b?Symbol.for("react.profiler"):60114,h=b?Symbol.for("react.provider"):60109,k=b?Symbol.for("react.context"):60110,l=b?Symbol.for("react.async_mode"):60111,m=b?Symbol.for("react.concurrent_mode"):60111,n=b?Symbol.for("react.forward_ref"):60112,p=b?Symbol.for("react.suspense"):60113,q=b? Symbol.for("react.suspense_list"):60120,r=b?Symbol.for("react.memo"):60115,t=b?Symbol.for("react.lazy"):60116,v=b?Symbol.for("react.block"):60121,w=b?Symbol.for("react.fundamental"):60117,x=b?Symbol.for("react.responder"):60118,y=b?Symbol.for("react.scope"):60119; function z(a){if("object"===typeof a&&null!==a){var u=a.$$typeof;switch(u){case c:switch(a=a.type,a){case l:case m:case e:case g:case f:case p:return a;default:switch(a=a&&a.$$typeof,a){case k:case n:case t:case r:case h:return a;default:return u}}case d:return u}}}function A(a){return z(a)===m}exports.AsyncMode=l;exports.ConcurrentMode=m;exports.ContextConsumer=k;exports.ContextProvider=h;exports.Element=c;exports.ForwardRef=n;exports.Fragment=e;exports.Lazy=t;exports.Memo=r;exports.Portal=d; exports.Profiler=g;exports.StrictMode=f;exports.Suspense=p;exports.isAsyncMode=function(a){return A(a)||z(a)===l};exports.isConcurrentMode=A;exports.isContextConsumer=function(a){return z(a)===k};exports.isContextProvider=function(a){return z(a)===h};exports.isElement=function(a){return"object"===typeof a&&null!==a&&a.$$typeof===c};exports.isForwardRef=function(a){return z(a)===n};exports.isFragment=function(a){return z(a)===e};exports.isLazy=function(a){return z(a)===t}; exports.isMemo=function(a){return z(a)===r};exports.isPortal=function(a){return z(a)===d};exports.isProfiler=function(a){return z(a)===g};exports.isStrictMode=function(a){return z(a)===f};exports.isSuspense=function(a){return z(a)===p}; exports.isValidElementType=function(a){return"string"===typeof a||"function"===typeof a||a===e||a===m||a===g||a===f||a===p||a===q||"object"===typeof a&&null!==a&&(a.$$typeof===t||a.$$typeof===r||a.$$typeof===h||a.$$typeof===k||a.$$typeof===n||a.$$typeof===w||a.$$typeof===x||a.$$typeof===y||a.$$typeof===v)};exports.typeOf=z; },{}],11:[function(require,module,exports){ (function (process){ 'use strict'; if (process.env.NODE_ENV === 'production') { module.exports = require('./cjs/react-is.production.min.js'); } else { module.exports = require('./cjs/react-is.development.js'); } }).call(this,require("hmr7eR")) },{"./cjs/react-is.development.js":9,"./cjs/react-is.production.min.js":10,"hmr7eR":1}],12:[function(require,module,exports){ (function (process){ /** * @license React * react.development.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; if (process.env.NODE_ENV !== "production") { (function() { 'use strict'; /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ if ( typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' && typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart === 'function' ) { __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error()); } var ReactVersion = '18.2.0'; // ATTENTION // When adding new symbols to this file, // Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols' // The Symbol used to tag the ReactElement-like types. var REACT_ELEMENT_TYPE = Symbol.for('react.element'); var REACT_PORTAL_TYPE = Symbol.for('react.portal'); var REACT_FRAGMENT_TYPE = Symbol.for('react.fragment'); var REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode'); var REACT_PROFILER_TYPE = Symbol.for('react.profiler'); var REACT_PROVIDER_TYPE = Symbol.for('react.provider'); var REACT_CONTEXT_TYPE = Symbol.for('react.context'); var REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref'); var REACT_SUSPENSE_TYPE = Symbol.for('react.suspense'); var REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list'); var REACT_MEMO_TYPE = Symbol.for('react.memo'); var REACT_LAZY_TYPE = Symbol.for('react.lazy'); var REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen'); var MAYBE_ITERATOR_SYMBOL = Symbol.iterator; var FAUX_ITERATOR_SYMBOL = '@@iterator'; function getIteratorFn(maybeIterable) { if (maybeIterable === null || typeof maybeIterable !== 'object') { return null; } var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]; if (typeof maybeIterator === 'function') { return maybeIterator; } return null; } /** * Keeps track of the current dispatcher. */ var ReactCurrentDispatcher = { /** * @internal * @type {ReactComponent} */ current: null }; /** * Keeps track of the current batch's configuration such as how long an update * should suspend for if it needs to. */ var ReactCurrentBatchConfig = { transition: null }; var ReactCurrentActQueue = { current: null, // Used to reproduce behavior of `batchedUpdates` in legacy mode. isBatchingLegacy: false, didScheduleLegacyUpdate: false }; /** * Keeps track of the current owner. * * The current owner is the component who should own any components that are * currently being constructed. */ var ReactCurrentOwner = { /** * @internal * @type {ReactComponent} */ current: null }; var ReactDebugCurrentFrame = {}; var currentExtraStackFrame = null; function setExtraStackFrame(stack) { { currentExtraStackFrame = stack; } } { ReactDebugCurrentFrame.setExtraStackFrame = function (stack) { { currentExtraStackFrame = stack; } }; // Stack implementation injected by the current renderer. ReactDebugCurrentFrame.getCurrentStack = null; ReactDebugCurrentFrame.getStackAddendum = function () { var stack = ''; // Add an extra top frame while an element is being validated if (currentExtraStackFrame) { stack += currentExtraStackFrame; } // Delegate to the injected renderer-specific implementation var impl = ReactDebugCurrentFrame.getCurrentStack; if (impl) { stack += impl() || ''; } return stack; }; } // ----------------------------------------------------------------------------- var enableScopeAPI = false; // Experimental Create Event Handle API. var enableCacheElement = false; var enableTransitionTracing = false; // No known bugs, but needs performance testing var enableLegacyHidden = false; // Enables unstable_avoidThisFallback feature in Fiber // stuff. Intended to enable React core members to more easily debug scheduling // issues in DEV builds. var enableDebugTracing = false; // Track which Fiber(s) schedule render work. var ReactSharedInternals = { ReactCurrentDispatcher: ReactCurrentDispatcher, ReactCurrentBatchConfig: ReactCurrentBatchConfig, ReactCurrentOwner: ReactCurrentOwner }; { ReactSharedInternals.ReactDebugCurrentFrame = ReactDebugCurrentFrame; ReactSharedInternals.ReactCurrentActQueue = ReactCurrentActQueue; } // by calls to these methods by a Babel plugin. // // In PROD (or in packages without access to React internals), // they are left as they are instead. function warn(format) { { { for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } printWarning('warn', format, args); } } } function error(format) { { { for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { args[_key2 - 1] = arguments[_key2]; } printWarning('error', format, args); } } } function printWarning(level, format, args) { // When changing this logic, you might want to also // update consoleWithStackDev.www.js as well. { var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; var stack = ReactDebugCurrentFrame.getStackAddendum(); if (stack !== '') { format += '%s'; args = args.concat([stack]); } // eslint-disable-next-line react-internal/safe-string-coercion var argsWithFormat = args.map(function (item) { return String(item); }); // Careful: RN currently depends on this prefix argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it // breaks IE9: https://github.com/facebook/react/issues/13610 // eslint-disable-next-line react-internal/no-production-logging Function.prototype.apply.call(console[level], console, argsWithFormat); } } var didWarnStateUpdateForUnmountedComponent = {}; function warnNoop(publicInstance, callerName) { { var _constructor = publicInstance.constructor; var componentName = _constructor && (_constructor.displayName || _constructor.name) || 'ReactClass'; var warningKey = componentName + "." + callerName; if (didWarnStateUpdateForUnmountedComponent[warningKey]) { return; } error("Can't call %s on a component that is not yet mounted. " + 'This is a no-op, but it might indicate a bug in your application. ' + 'Instead, assign to `this.state` directly or define a `state = {};` ' + 'class property with the desired state in the %s component.', callerName, componentName); didWarnStateUpdateForUnmountedComponent[warningKey] = true; } } /** * This is the abstract API for an update queue. */ var ReactNoopUpdateQueue = { /** * Checks whether or not this composite component is mounted. * @param {ReactClass} publicInstance The instance we want to test. * @return {boolean} True if mounted, false otherwise. * @protected * @final */ isMounted: function (publicInstance) { return false; }, /** * Forces an update. This should only be invoked when it is known with * certainty that we are **not** in a DOM transaction. * * You may want to call this when you know that some deeper aspect of the * component's state has changed but `setState` was not called. * * This will not invoke `shouldComponentUpdate`, but it will invoke * `componentWillUpdate` and `componentDidUpdate`. * * @param {ReactClass} publicInstance The instance that should rerender. * @param {?function} callback Called after component is updated. * @param {?string} callerName name of the calling function in the public API. * @internal */ enqueueForceUpdate: function (publicInstance, callback, callerName) { warnNoop(publicInstance, 'forceUpdate'); }, /** * Replaces all of the state. Always use this or `setState` to mutate state. * You should treat `this.state` as immutable. * * There is no guarantee that `this.state` will be immediately updated, so * accessing `this.state` after calling this method may return the old value. * * @param {ReactClass} publicInstance The instance that should rerender. * @param {object} completeState Next state. * @param {?function} callback Called after component is updated. * @param {?string} callerName name of the calling function in the public API. * @internal */ enqueueReplaceState: function (publicInstance, completeState, callback, callerName) { warnNoop(publicInstance, 'replaceState'); }, /** * Sets a subset of the state. This only exists because _pendingState is * internal. This provides a merging strategy that is not available to deep * properties which is confusing. TODO: Expose pendingState or don't use it * during the merge. * * @param {ReactClass} publicInstance The instance that should rerender. * @param {object} partialState Next partial state to be merged with state. * @param {?function} callback Called after component is updated. * @param {?string} Name of the calling function in the public API. * @internal */ enqueueSetState: function (publicInstance, partialState, callback, callerName) { warnNoop(publicInstance, 'setState'); } }; var assign = Object.assign; var emptyObject = {}; { Object.freeze(emptyObject); } /** * Base class helpers for the updating state of a component. */ function Component(props, context, updater) { this.props = props; this.context = context; // If a component has string refs, we will assign a different object later. this.refs = emptyObject; // We initialize the default updater but the real one gets injected by the // renderer. this.updater = updater || ReactNoopUpdateQueue; } Component.prototype.isReactComponent = {}; /** * Sets a subset of the state. Always use this to mutate * state. You should treat `this.state` as immutable. * * There is no guarantee that `this.state` will be immediately updated, so * accessing `this.state` after calling this method may return the old value. * * There is no guarantee that calls to `setState` will run synchronously, * as they may eventually be batched together. You can provide an optional * callback that will be executed when the call to setState is actually * completed. * * When a function is provided to setState, it will be called at some point in * the future (not synchronously). It will be called with the up to date * component arguments (state, props, context). These values can be different * from this.* because your function may be called after receiveProps but before * shouldComponentUpdate, and this new state, props, and context will not yet be * assigned to this. * * @param {object|function} partialState Next partial state or function to * produce next partial state to be merged with current state. * @param {?function} callback Called after state is updated. * @final * @protected */ Component.prototype.setState = function (partialState, callback) { if (typeof partialState !== 'object' && typeof partialState !== 'function' && partialState != null) { throw new Error('setState(...): takes an object of state variables to update or a ' + 'function which returns an object of state variables.'); } this.updater.enqueueSetState(this, partialState, callback, 'setState'); }; /** * Forces an update. This should only be invoked when it is known with * certainty that we are **not** in a DOM transaction. * * You may want to call this when you know that some deeper aspect of the * component's state has changed but `setState` was not called. * * This will not invoke `shouldComponentUpdate`, but it will invoke * `componentWillUpdate` and `componentDidUpdate`. * * @param {?function} callback Called after update is complete. * @final * @protected */ Component.prototype.forceUpdate = function (callback) { this.updater.enqueueForceUpdate(this, callback, 'forceUpdate'); }; /** * Deprecated APIs. These APIs used to exist on classic React classes but since * we would like to deprecate them, we're not going to move them over to this * modern base class. Instead, we define a getter that warns if it's accessed. */ { var deprecatedAPIs = { isMounted: ['isMounted', 'Instead, make sure to clean up subscriptions and pending requests in ' + 'componentWillUnmount to prevent memory leaks.'], replaceState: ['replaceState', 'Refactor your code to use setState instead (see ' + 'https://github.com/facebook/react/issues/3236).'] }; var defineDeprecationWarning = function (methodName, info) { Object.defineProperty(Component.prototype, methodName, { get: function () { warn('%s(...) is deprecated in plain JavaScript React classes. %s', info[0], info[1]); return undefined; } }); }; for (var fnName in deprecatedAPIs) { if (deprecatedAPIs.hasOwnProperty(fnName)) { defineDeprecationWarning(fnName, deprecatedAPIs[fnName]); } } } function ComponentDummy() {} ComponentDummy.prototype = Component.prototype; /** * Convenience component with default shallow equality check for sCU. */ function PureComponent(props, context, updater) { this.props = props; this.context = context; // If a component has string refs, we will assign a different object later. this.refs = emptyObject; this.updater = updater || ReactNoopUpdateQueue; } var pureComponentPrototype = PureComponent.prototype = new ComponentDummy(); pureComponentPrototype.constructor = PureComponent; // Avoid an extra prototype jump for these methods. assign(pureComponentPrototype, Component.prototype); pureComponentPrototype.isPureReactComponent = true; // an immutable object with a single mutable value function createRef() { var refObject = { current: null }; { Object.seal(refObject); } return refObject; } var isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare function isArray(a) { return isArrayImpl(a); } /* * The `'' + value` pattern (used in in perf-sensitive code) throws for Symbol * and Temporal.* types. See https://github.com/facebook/react/pull/22064. * * The functions in this module will throw an easier-to-understand, * easier-to-debug exception with a clear errors message message explaining the * problem. (Instead of a confusing exception thrown inside the implementation * of the `value` object). */ // $FlowFixMe only called in DEV, so void return is not possible. function typeName(value) { { // toStringTag is needed for namespaced types like Temporal.Instant var hasToStringTag = typeof Symbol === 'function' && Symbol.toStringTag; var type = hasToStringTag && value[Symbol.toStringTag] || value.constructor.name || 'Object'; return type; } } // $FlowFixMe only called in DEV, so void return is not possible. function willCoercionThrow(value) { { try { testStringCoercion(value); return false; } catch (e) { return true; } } } function testStringCoercion(value) { // If you ended up here by following an exception call stack, here's what's // happened: you supplied an object or symbol value to React (as a prop, key, // DOM attribute, CSS property, string ref, etc.) and when React tried to // coerce it to a string using `'' + value`, an exception was thrown. // // The most common types that will cause this exception are `Symbol` instances // and Temporal objects like `Temporal.Instant`. But any object that has a // `valueOf` or `[Symbol.toPrimitive]` method that throws will also cause this // exception. (Library authors do this to prevent users from using built-in // numeric operators like `+` or comparison operators like `>=` because custom // methods are needed to perform accurate arithmetic or comparison.) // // To fix the problem, coerce this object or symbol value to a string before // passing it to React. The most reliable way is usually `String(value)`. // // To find which value is throwing, check the browser or debugger console. // Before this exception was thrown, there should be `console.error` output // that shows the type (Symbol, Temporal.PlainDate, etc.) that caused the // problem and how that type was used: key, atrribute, input value prop, etc. // In most cases, this console output also shows the component and its // ancestor components where the exception happened. // // eslint-disable-next-line react-internal/safe-string-coercion return '' + value; } function checkKeyStringCoercion(value) { { if (willCoercionThrow(value)) { error('The provided key is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', typeName(value)); return testStringCoercion(value); // throw (to help callers find troubleshooting comments) } } } function getWrappedName(outerType, innerType, wrapperName) { var displayName = outerType.displayName; if (displayName) { return displayName; } var functionName = innerType.displayName || innerType.name || ''; return functionName !== '' ? wrapperName + "(" + functionName + ")" : wrapperName; } // Keep in sync with react-reconciler/getComponentNameFromFiber function getContextName(type) { return type.displayName || 'Context'; } // Note that the reconciler package should generally prefer to use getComponentNameFromFiber() instead. function getComponentNameFromType(type) { if (type == null) { // Host root, text node or just invalid type. return null; } { if (typeof type.tag === 'number') { error('Received an unexpected object in getComponentNameFromType(). ' + 'This is likely a bug in React. Please file an issue.'); } } if (typeof type === 'function') { return type.displayName || type.name || null; } if (typeof type === 'string') { return type; } switch (type) { case REACT_FRAGMENT_TYPE: return 'Fragment'; case REACT_PORTAL_TYPE: return 'Portal'; case REACT_PROFILER_TYPE: return 'Profiler'; case REACT_STRICT_MODE_TYPE: return 'StrictMode'; case REACT_SUSPENSE_TYPE: return 'Suspense'; case REACT_SUSPENSE_LIST_TYPE: return 'SuspenseList'; } if (typeof type === 'object') { switch (type.$$typeof) { case REACT_CONTEXT_TYPE: var context = type; return getContextName(context) + '.Consumer'; case REACT_PROVIDER_TYPE: var provider = type; return getContextName(provider._context) + '.Provider'; case REACT_FORWARD_REF_TYPE: return getWrappedName(type, type.render, 'ForwardRef'); case REACT_MEMO_TYPE: var outerName = type.displayName || null; if (outerName !== null) { return outerName; } return getComponentNameFromType(type.type) || 'Memo'; case REACT_LAZY_TYPE: { var lazyComponent = type; var payload = lazyComponent._payload; var init = lazyComponent._init; try { return getComponentNameFromType(init(payload)); } catch (x) { return null; } } // eslint-disable-next-line no-fallthrough } } return null; } var hasOwnProperty = Object.prototype.hasOwnProperty; var RESERVED_PROPS = { key: true, ref: true, __self: true, __source: true }; var specialPropKeyWarningShown, specialPropRefWarningShown, didWarnAboutStringRefs; { didWarnAboutStringRefs = {}; } function hasValidRef(config) { { if (hasOwnProperty.call(config, 'ref')) { var getter = Object.getOwnPropertyDescriptor(config, 'ref').get; if (getter && getter.isReactWarning) { return false; } } } return config.ref !== undefined; } function hasValidKey(config) { { if (hasOwnProperty.call(config, 'key')) { var getter = Object.getOwnPropertyDescriptor(config, 'key').get; if (getter && getter.isReactWarning) { return false; } } } return config.key !== undefined; } function defineKeyPropWarningGetter(props, displayName) { var warnAboutAccessingKey = function () { { if (!specialPropKeyWarningShown) { specialPropKeyWarningShown = true; error('%s: `key` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://reactjs.org/link/special-props)', displayName); } } }; warnAboutAccessingKey.isReactWarning = true; Object.defineProperty(props, 'key', { get: warnAboutAccessingKey, configurable: true }); } function defineRefPropWarningGetter(props, displayName) { var warnAboutAccessingRef = function () { { if (!specialPropRefWarningShown) { specialPropRefWarningShown = true; error('%s: `ref` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://reactjs.org/link/special-props)', displayName); } } }; warnAboutAccessingRef.isReactWarning = true; Object.defineProperty(props, 'ref', { get: warnAboutAccessingRef, configurable: true }); } function warnIfStringRefCannotBeAutoConverted(config) { { if (typeof config.ref === 'string' && ReactCurrentOwner.current && config.__self && ReactCurrentOwner.current.stateNode !== config.__self) { var componentName = getComponentNameFromType(ReactCurrentOwner.current.type); if (!didWarnAboutStringRefs[componentName]) { error('Component "%s" contains the string ref "%s". ' + 'Support for string refs will be removed in a future major release. ' + 'This case cannot be automatically converted to an arrow function. ' + 'We ask you to manually fix this case by using useRef() or createRef() instead. ' + 'Learn more about using refs safely here: ' + 'https://reactjs.org/link/strict-mode-string-ref', componentName, config.ref); didWarnAboutStringRefs[componentName] = true; } } } } /** * Factory method to create a new React element. This no longer adheres to * the class pattern, so do not use new to call it. Also, instanceof check * will not work. Instead test $$typeof field against Symbol.for('react.element') to check * if something is a React Element. * * @param {*} type * @param {*} props * @param {*} key * @param {string|object} ref * @param {*} owner * @param {*} self A *temporary* helper to detect places where `this` is * different from the `owner` when React.createElement is called, so that we * can warn. We want to get rid of owner and replace string `ref`s with arrow * functions, and as long as `this` and owner are the same, there will be no * change in behavior. * @param {*} source An annotation object (added by a transpiler or otherwise) * indicating filename, line number, and/or other information. * @internal */ var ReactElement = function (type, key, ref, self, source, owner, props) { var element = { // This tag allows us to uniquely identify this as a React Element $$typeof: REACT_ELEMENT_TYPE, // Built-in properties that belong on the element type: type, key: key, ref: ref, props: props, // Record the component responsible for creating this element. _owner: owner }; { // The validation flag is currently mutative. We put it on // an external backing store so that we can freeze the whole object. // This can be replaced with a WeakMap once they are implemented in // commonly used development environments. element._store = {}; // To make comparing ReactElements easier for testing purposes, we make // the validation flag non-enumerable (where possible, which should // include every environment we run tests in), so the test framework // ignores it. Object.defineProperty(element._store, 'validated', { configurable: false, enumerable: false, writable: true, value: false }); // self and source are DEV only properties. Object.defineProperty(element, '_self', { configurable: false, enumerable: false, writable: false, value: self }); // Two elements created in two different places should be considered // equal for testing purposes and therefore we hide it from enumeration. Object.defineProperty(element, '_source', { configurable: false, enumerable: false, writable: false, value: source }); if (Object.freeze) { Object.freeze(element.props); Object.freeze(element); } } return element; }; /** * Create and return a new ReactElement of the given type. * See https://reactjs.org/docs/react-api.html#createelement */ function createElement(type, config, children) { var propName; // Reserved names are extracted var props = {}; var key = null; var ref = null; var self = null; var source = null; if (config != null) { if (hasValidRef(config)) { ref = config.ref; { warnIfStringRefCannotBeAutoConverted(config); } } if (hasValidKey(config)) { { checkKeyStringCoercion(config.key); } key = '' + config.key; } self = config.__self === undefined ? null : config.__self; source = config.__source === undefined ? null : config.__source; // Remaining properties are added to a new props object for (propName in config) { if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { props[propName] = config[propName]; } } } // Children can be more than one argument, and those are transferred onto // the newly allocated props object. var childrenLength = arguments.length - 2; if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { var childArray = Array(childrenLength); for (var i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } { if (Object.freeze) { Object.freeze(childArray); } } props.children = childArray; } // Resolve default props if (type && type.defaultProps) { var defaultProps = type.defaultProps; for (propName in defaultProps) { if (props[propName] === undefined) { props[propName] = defaultProps[propName]; } } } { if (key || ref) { var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type; if (key) { defineKeyPropWarningGetter(props, displayName); } if (ref) { defineRefPropWarningGetter(props, displayName); } } } return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props); } function cloneAndReplaceKey(oldElement, newKey) { var newElement = ReactElement(oldElement.type, newKey, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, oldElement.props); return newElement; } /** * Clone and return a new ReactElement using element as the starting point. * See https://reactjs.org/docs/react-api.html#cloneelement */ function cloneElement(element, config, children) { if (element === null || element === undefined) { throw new Error("React.cloneElement(...): The argument must be a React element, but you passed " + element + "."); } var propName; // Original props are copied var props = assign({}, element.props); // Reserved names are extracted var key = element.key; var ref = element.ref; // Self is preserved since the owner is preserved. var self = element._self; // Source is preserved since cloneElement is unlikely to be targeted by a // transpiler, and the original source is probably a better indicator of the // true owner. var source = element._source; // Owner will be preserved, unless ref is overridden var owner = element._owner; if (config != null) { if (hasValidRef(config)) { // Silently steal the ref from the parent. ref = config.ref; owner = ReactCurrentOwner.current; } if (hasValidKey(config)) { { checkKeyStringCoercion(config.key); } key = '' + config.key; } // Remaining properties override existing props var defaultProps; if (element.type && element.type.defaultProps) { defaultProps = element.type.defaultProps; } for (propName in config) { if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { if (config[propName] === undefined && defaultProps !== undefined) { // Resolve default props props[propName] = defaultProps[propName]; } else { props[propName] = config[propName]; } } } } // Children can be more than one argument, and those are transferred onto // the newly allocated props object. var childrenLength = arguments.length - 2; if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { var childArray = Array(childrenLength); for (var i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } props.children = childArray; } return ReactElement(element.type, key, ref, self, source, owner, props); } /** * Verifies the object is a ReactElement. * See https://reactjs.org/docs/react-api.html#isvalidelement * @param {?object} object * @return {boolean} True if `object` is a ReactElement. * @final */ function isValidElement(object) { return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE; } var SEPARATOR = '.'; var SUBSEPARATOR = ':'; /** * Escape and wrap key so it is safe to use as a reactid * * @param {string} key to be escaped. * @return {string} the escaped key. */ function escape(key) { var escapeRegex = /[=:]/g; var escaperLookup = { '=': '=0', ':': '=2' }; var escapedString = key.replace(escapeRegex, function (match) { return escaperLookup[match]; }); return '$' + escapedString; } /** * TODO: Test that a single child and an array with one item have the same key * pattern. */ var didWarnAboutMaps = false; var userProvidedKeyEscapeRegex = /\/+/g; function escapeUserProvidedKey(text) { return text.replace(userProvidedKeyEscapeRegex, '$&/'); } /** * Generate a key string that identifies a element within a set. * * @param {*} element A element that could contain a manual key. * @param {number} index Index that is used if a manual key is not provided. * @return {string} */ function getElementKey(element, index) { // Do some typechecking here since we call this blindly. We want to ensure // that we don't block potential future ES APIs. if (typeof element === 'object' && element !== null && element.key != null) { // Explicit key { checkKeyStringCoercion(element.key); } return escape('' + element.key); } // Implicit key determined by the index in the set return index.toString(36); } function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) { var type = typeof children; if (type === 'undefined' || type === 'boolean') { // All of the above are perceived as null. children = null; } var invokeCallback = false; if (children === null) { invokeCallback = true; } else { switch (type) { case 'string': case 'number': invokeCallback = true; break; case 'object': switch (children.$$typeof) { case REACT_ELEMENT_TYPE: case REACT_PORTAL_TYPE: invokeCallback = true; } } } if (invokeCallback) { var _child = children; var mappedChild = callback(_child); // If it's the only child, treat the name as if it was wrapped in an array // so that it's consistent if the number of children grows: var childKey = nameSoFar === '' ? SEPARATOR + getElementKey(_child, 0) : nameSoFar; if (isArray(mappedChild)) { var escapedChildKey = ''; if (childKey != null) { escapedChildKey = escapeUserProvidedKey(childKey) + '/'; } mapIntoArray(mappedChild, array, escapedChildKey, '', function (c) { return c; }); } else if (mappedChild != null) { if (isValidElement(mappedChild)) { { // The `if` statement here prevents auto-disabling of the safe // coercion ESLint rule, so we must manually disable it below. // $FlowFixMe Flow incorrectly thinks React.Portal doesn't have a key if (mappedChild.key && (!_child || _child.key !== mappedChild.key)) { checkKeyStringCoercion(mappedChild.key); } } mappedChild = cloneAndReplaceKey(mappedChild, // Keep both the (mapped) and old keys if they differ, just as // traverseAllChildren used to do for objects as children escapedPrefix + ( // $FlowFixMe Flow incorrectly thinks React.Portal doesn't have a key mappedChild.key && (!_child || _child.key !== mappedChild.key) ? // $FlowFixMe Flow incorrectly thinks existing element's key can be a number // eslint-disable-next-line react-internal/safe-string-coercion escapeUserProvidedKey('' + mappedChild.key) + '/' : '') + childKey); } array.push(mappedChild); } return 1; } var child; var nextName; var subtreeCount = 0; // Count of children found in the current subtree. var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR; if (isArray(children)) { for (var i = 0; i < children.length; i++) { child = children[i]; nextName = nextNamePrefix + getElementKey(child, i); subtreeCount += mapIntoArray(child, array, escapedPrefix, nextName, callback); } } else { var iteratorFn = getIteratorFn(children); if (typeof iteratorFn === 'function') { var iterableChildren = children; { // Warn about using Maps as children if (iteratorFn === iterableChildren.entries) { if (!didWarnAboutMaps) { warn('Using Maps as children is not supported. ' + 'Use an array of keyed ReactElements instead.'); } didWarnAboutMaps = true; } } var iterator = iteratorFn.call(iterableChildren); var step; var ii = 0; while (!(step = iterator.next()).done) { child = step.value; nextName = nextNamePrefix + getElementKey(child, ii++); subtreeCount += mapIntoArray(child, array, escapedPrefix, nextName, callback); } } else if (type === 'object') { // eslint-disable-next-line react-internal/safe-string-coercion var childrenString = String(children); throw new Error("Objects are not valid as a React child (found: " + (childrenString === '[object Object]' ? 'object with keys {' + Object.keys(children).join(', ') + '}' : childrenString) + "). " + 'If you meant to render a collection of children, use an array ' + 'instead.'); } } return subtreeCount; } /** * Maps children that are typically specified as `props.children`. * * See https://reactjs.org/docs/react-api.html#reactchildrenmap * * The provided mapFunction(child, index) will be called for each * leaf child. * * @param {?*} children Children tree container. * @param {function(*, int)} func The map function. * @param {*} context Context for mapFunction. * @return {object} Object containing the ordered map of results. */ function mapChildren(children, func, context) { if (children == null) { return children; } var result = []; var count = 0; mapIntoArray(children, result, '', '', function (child) { return func.call(context, child, count++); }); return result; } /** * Count the number of children that are typically specified as * `props.children`. * * See https://reactjs.org/docs/react-api.html#reactchildrencount * * @param {?*} children Children tree container. * @return {number} The number of children. */ function countChildren(children) { var n = 0; mapChildren(children, function () { n++; // Don't return anything }); return n; } /** * Iterates through children that are typically specified as `props.children`. * * See https://reactjs.org/docs/react-api.html#reactchildrenforeach * * The provided forEachFunc(child, index) will be called for each * leaf child. * * @param {?*} children Children tree container. * @param {function(*, int)} forEachFunc * @param {*} forEachContext Context for forEachContext. */ function forEachChildren(children, forEachFunc, forEachContext) { mapChildren(children, function () { forEachFunc.apply(this, arguments); // Don't return anything. }, forEachContext); } /** * Flatten a children object (typically specified as `props.children`) and * return an array with appropriately re-keyed children. * * See https://reactjs.org/docs/react-api.html#reactchildrentoarray */ function toArray(children) { return mapChildren(children, function (child) { return child; }) || []; } /** * Returns the first child in a collection of children and verifies that there * is only one child in the collection. * * See https://reactjs.org/docs/react-api.html#reactchildrenonly * * The current implementation of this function assumes that a single child gets * passed without a wrapper, but the purpose of this helper function is to * abstract away the particular structure of children. * * @param {?object} children Child collection structure. * @return {ReactElement} The first and only `ReactElement` contained in the * structure. */ function onlyChild(children) { if (!isValidElement(children)) { throw new Error('React.Children.only expected to receive a single React element child.'); } return children; } function createContext(defaultValue) { // TODO: Second argument used to be an optional `calculateChangedBits` // function. Warn to reserve for future use? var context = { $$typeof: REACT_CONTEXT_TYPE, // As a workaround to support multiple concurrent renderers, we categorize // some renderers as primary and others as secondary. We only expect // there to be two concurrent renderers at most: React Native (primary) and // Fabric (secondary); React DOM (primary) and React ART (secondary). // Secondary renderers store their context values on separate fields. _currentValue: defaultValue, _currentValue2: defaultValue, // Used to track how many concurrent renderers this context currently // supports within in a single renderer. Such as parallel server rendering. _threadCount: 0, // These are circular Provider: null, Consumer: null, // Add these to use same hidden class in VM as ServerContext _defaultValue: null, _globalName: null }; context.Provider = { $$typeof: REACT_PROVIDER_TYPE, _context: context }; var hasWarnedAboutUsingNestedContextConsumers = false; var hasWarnedAboutUsingConsumerProvider = false; var hasWarnedAboutDisplayNameOnConsumer = false; { // A separate object, but proxies back to the original context object for // backwards compatibility. It has a different $$typeof, so we can properly // warn for the incorrect usage of Context as a Consumer. var Consumer = { $$typeof: REACT_CONTEXT_TYPE, _context: context }; // $FlowFixMe: Flow complains about not setting a value, which is intentional here Object.defineProperties(Consumer, { Provider: { get: function () { if (!hasWarnedAboutUsingConsumerProvider) { hasWarnedAboutUsingConsumerProvider = true; error('Rendering is not supported and will be removed in ' + 'a future major release. Did you mean to render instead?'); } return context.Provider; }, set: function (_Provider) { context.Provider = _Provider; } }, _currentValue: { get: function () { return context._currentValue; }, set: function (_currentValue) { context._currentValue = _currentValue; } }, _currentValue2: { get: function () { return context._currentValue2; }, set: function (_currentValue2) { context._currentValue2 = _currentValue2; } }, _threadCount: { get: function () { return context._threadCount; }, set: function (_threadCount) { context._threadCount = _threadCount; } }, Consumer: { get: function () { if (!hasWarnedAboutUsingNestedContextConsumers) { hasWarnedAboutUsingNestedContextConsumers = true; error('Rendering is not supported and will be removed in ' + 'a future major release. Did you mean to render instead?'); } return context.Consumer; } }, displayName: { get: function () { return context.displayName; }, set: function (displayName) { if (!hasWarnedAboutDisplayNameOnConsumer) { warn('Setting `displayName` on Context.Consumer has no effect. ' + "You should set it directly on the context with Context.displayName = '%s'.", displayName); hasWarnedAboutDisplayNameOnConsumer = true; } } } }); // $FlowFixMe: Flow complains about missing properties because it doesn't understand defineProperty context.Consumer = Consumer; } { context._currentRenderer = null; context._currentRenderer2 = null; } return context; } var Uninitialized = -1; var Pending = 0; var Resolved = 1; var Rejected = 2; function lazyInitializer(payload) { if (payload._status === Uninitialized) { var ctor = payload._result; var thenable = ctor(); // Transition to the next state. // This might throw either because it's missing or throws. If so, we treat it // as still uninitialized and try again next time. Which is the same as what // happens if the ctor or any wrappers processing the ctor throws. This might // end up fixing it if the resolution was a concurrency bug. thenable.then(function (moduleObject) { if (payload._status === Pending || payload._status === Uninitialized) { // Transition to the next state. var resolved = payload; resolved._status = Resolved; resolved._result = moduleObject; } }, function (error) { if (payload._status === Pending || payload._status === Uninitialized) { // Transition to the next state. var rejected = payload; rejected._status = Rejected; rejected._result = error; } }); if (payload._status === Uninitialized) { // In case, we're still uninitialized, then we're waiting for the thenable // to resolve. Set it as pending in the meantime. var pending = payload; pending._status = Pending; pending._result = thenable; } } if (payload._status === Resolved) { var moduleObject = payload._result; { if (moduleObject === undefined) { error('lazy: Expected the result of a dynamic imp' + 'ort() call. ' + 'Instead received: %s\n\nYour code should look like: \n ' + // Break up imports to avoid accidentally parsing them as dependencies. 'const MyComponent = lazy(() => imp' + "ort('./MyComponent'))\n\n" + 'Did you accidentally put curly braces around the import?', moduleObject); } } { if (!('default' in moduleObject)) { error('lazy: Expected the result of a dynamic imp' + 'ort() call. ' + 'Instead received: %s\n\nYour code should look like: \n ' + // Break up imports to avoid accidentally parsing them as dependencies. 'const MyComponent = lazy(() => imp' + "ort('./MyComponent'))", moduleObject); } } return moduleObject.default; } else { throw payload._result; } } function lazy(ctor) { var payload = { // We use these fields to store the result. _status: Uninitialized, _result: ctor }; var lazyType = { $$typeof: REACT_LAZY_TYPE, _payload: payload, _init: lazyInitializer }; { // In production, this would just set it on the object. var defaultProps; var propTypes; // $FlowFixMe Object.defineProperties(lazyType, { defaultProps: { configurable: true, get: function () { return defaultProps; }, set: function (newDefaultProps) { error('React.lazy(...): It is not supported to assign `defaultProps` to ' + 'a lazy component import. Either specify them where the component ' + 'is defined, or create a wrapping component around it.'); defaultProps = newDefaultProps; // Match production behavior more closely: // $FlowFixMe Object.defineProperty(lazyType, 'defaultProps', { enumerable: true }); } }, propTypes: { configurable: true, get: function () { return propTypes; }, set: function (newPropTypes) { error('React.lazy(...): It is not supported to assign `propTypes` to ' + 'a lazy component import. Either specify them where the component ' + 'is defined, or create a wrapping component around it.'); propTypes = newPropTypes; // Match production behavior more closely: // $FlowFixMe Object.defineProperty(lazyType, 'propTypes', { enumerable: true }); } } }); } return lazyType; } function forwardRef(render) { { if (render != null && render.$$typeof === REACT_MEMO_TYPE) { error('forwardRef requires a render function but received a `memo` ' + 'component. Instead of forwardRef(memo(...)), use ' + 'memo(forwardRef(...)).'); } else if (typeof render !== 'function') { error('forwardRef requires a render function but was given %s.', render === null ? 'null' : typeof render); } else { if (render.length !== 0 && render.length !== 2) { error('forwardRef render functions accept exactly two parameters: props and ref. %s', render.length === 1 ? 'Did you forget to use the ref parameter?' : 'Any additional parameter will be undefined.'); } } if (render != null) { if (render.defaultProps != null || render.propTypes != null) { error('forwardRef render functions do not support propTypes or defaultProps. ' + 'Did you accidentally pass a React component?'); } } } var elementType = { $$typeof: REACT_FORWARD_REF_TYPE, render: render }; { var ownName; Object.defineProperty(elementType, 'displayName', { enumerable: false, configurable: true, get: function () { return ownName; }, set: function (name) { ownName = name; // The inner component shouldn't inherit this display name in most cases, // because the component may be used elsewhere. // But it's nice for anonymous functions to inherit the name, // so that our component-stack generation logic will display their frames. // An anonymous function generally suggests a pattern like: // React.forwardRef((props, ref) => {...}); // This kind of inner function is not used elsewhere so the side effect is okay. if (!render.name && !render.displayName) { render.displayName = name; } } }); } return elementType; } var REACT_MODULE_REFERENCE; { REACT_MODULE_REFERENCE = Symbol.for('react.module.reference'); } function isValidElementType(type) { if (typeof type === 'string' || typeof type === 'function') { return true; } // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill). if (type === REACT_FRAGMENT_TYPE || type === REACT_PROFILER_TYPE || enableDebugTracing || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || enableLegacyHidden || type === REACT_OFFSCREEN_TYPE || enableScopeAPI || enableCacheElement || enableTransitionTracing ) { return true; } if (typeof type === 'object' && type !== null) { if (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object // types supported by any Flight configuration anywhere since // we don't know which Flight build this will end up being used // with. type.$$typeof === REACT_MODULE_REFERENCE || type.getModuleId !== undefined) { return true; } } return false; } function memo(type, compare) { { if (!isValidElementType(type)) { error('memo: The first argument must be a component. Instead ' + 'received: %s', type === null ? 'null' : typeof type); } } var elementType = { $$typeof: REACT_MEMO_TYPE, type: type, compare: compare === undefined ? null : compare }; { var ownName; Object.defineProperty(elementType, 'displayName', { enumerable: false, configurable: true, get: function () { return ownName; }, set: function (name) { ownName = name; // The inner component shouldn't inherit this display name in most cases, // because the component may be used elsewhere. // But it's nice for anonymous functions to inherit the name, // so that our component-stack generation logic will display their frames. // An anonymous function generally suggests a pattern like: // React.memo((props) => {...}); // This kind of inner function is not used elsewhere so the side effect is okay. if (!type.name && !type.displayName) { type.displayName = name; } } }); } return elementType; } function resolveDispatcher() { var dispatcher = ReactCurrentDispatcher.current; { if (dispatcher === null) { error('Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + ' one of the following reasons:\n' + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + '2. You might be breaking the Rules of Hooks\n' + '3. You might have more than one copy of React in the same app\n' + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.'); } } // Will result in a null access error if accessed outside render phase. We // intentionally don't throw our own error because this is in a hot path. // Also helps ensure this is inlined. return dispatcher; } function useContext(Context) { var dispatcher = resolveDispatcher(); { // TODO: add a more generic warning for invalid values. if (Context._context !== undefined) { var realContext = Context._context; // Don't deduplicate because this legitimately causes bugs // and nobody should be using this in existing code. if (realContext.Consumer === Context) { error('Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be ' + 'removed in a future major release. Did you mean to call useContext(Context) instead?'); } else if (realContext.Provider === Context) { error('Calling useContext(Context.Provider) is not supported. ' + 'Did you mean to call useContext(Context) instead?'); } } } return dispatcher.useContext(Context); } function useState(initialState) { var dispatcher = resolveDispatcher(); return dispatcher.useState(initialState); } function useReducer(reducer, initialArg, init) { var dispatcher = resolveDispatcher(); return dispatcher.useReducer(reducer, initialArg, init); } function useRef(initialValue) { var dispatcher = resolveDispatcher(); return dispatcher.useRef(initialValue); } function useEffect(create, deps) { var dispatcher = resolveDispatcher(); return dispatcher.useEffect(create, deps); } function useInsertionEffect(create, deps) { var dispatcher = resolveDispatcher(); return dispatcher.useInsertionEffect(create, deps); } function useLayoutEffect(create, deps) { var dispatcher = resolveDispatcher(); return dispatcher.useLayoutEffect(create, deps); } function useCallback(callback, deps) { var dispatcher = resolveDispatcher(); return dispatcher.useCallback(callback, deps); } function useMemo(create, deps) { var dispatcher = resolveDispatcher(); return dispatcher.useMemo(create, deps); } function useImperativeHandle(ref, create, deps) { var dispatcher = resolveDispatcher(); return dispatcher.useImperativeHandle(ref, create, deps); } function useDebugValue(value, formatterFn) { { var dispatcher = resolveDispatcher(); return dispatcher.useDebugValue(value, formatterFn); } } function useTransition() { var dispatcher = resolveDispatcher(); return dispatcher.useTransition(); } function useDeferredValue(value) { var dispatcher = resolveDispatcher(); return dispatcher.useDeferredValue(value); } function useId() { var dispatcher = resolveDispatcher(); return dispatcher.useId(); } function useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) { var dispatcher = resolveDispatcher(); return dispatcher.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); } // Helpers to patch console.logs to avoid logging during side-effect free // replaying on render function. This currently only patches the object // lazily which won't cover if the log function was extracted eagerly. // We could also eagerly patch the method. var disabledDepth = 0; var prevLog; var prevInfo; var prevWarn; var prevError; var prevGroup; var prevGroupCollapsed; var prevGroupEnd; function disabledLog() {} disabledLog.__reactDisabledLog = true; function disableLogs() { { if (disabledDepth === 0) { /* eslint-disable react-internal/no-production-logging */ prevLog = console.log; prevInfo = console.info; prevWarn = console.warn; prevError = console.error; prevGroup = console.group; prevGroupCollapsed = console.groupCollapsed; prevGroupEnd = console.groupEnd; // https://github.com/facebook/react/issues/19099 var props = { configurable: true, enumerable: true, value: disabledLog, writable: true }; // $FlowFixMe Flow thinks console is immutable. Object.defineProperties(console, { info: props, log: props, warn: props, error: props, group: props, groupCollapsed: props, groupEnd: props }); /* eslint-enable react-internal/no-production-logging */ } disabledDepth++; } } function reenableLogs() { { disabledDepth--; if (disabledDepth === 0) { /* eslint-disable react-internal/no-production-logging */ var props = { configurable: true, enumerable: true, writable: true }; // $FlowFixMe Flow thinks console is immutable. Object.defineProperties(console, { log: assign({}, props, { value: prevLog }), info: assign({}, props, { value: prevInfo }), warn: assign({}, props, { value: prevWarn }), error: assign({}, props, { value: prevError }), group: assign({}, props, { value: prevGroup }), groupCollapsed: assign({}, props, { value: prevGroupCollapsed }), groupEnd: assign({}, props, { value: prevGroupEnd }) }); /* eslint-enable react-internal/no-production-logging */ } if (disabledDepth < 0) { error('disabledDepth fell below zero. ' + 'This is a bug in React. Please file an issue.'); } } } var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher; var prefix; function describeBuiltInComponentFrame(name, source, ownerFn) { { if (prefix === undefined) { // Extract the VM specific prefix used by each line. try { throw Error(); } catch (x) { var match = x.stack.trim().match(/\n( *(at )?)/); prefix = match && match[1] || ''; } } // We use the prefix to ensure our stacks line up with native stack frames. return '\n' + prefix + name; } } var reentry = false; var componentFrameCache; { var PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map; componentFrameCache = new PossiblyWeakMap(); } function describeNativeComponentFrame(fn, construct) { // If something asked for a stack inside a fake render, it should get ignored. if ( !fn || reentry) { return ''; } { var frame = componentFrameCache.get(fn); if (frame !== undefined) { return frame; } } var control; reentry = true; var previousPrepareStackTrace = Error.prepareStackTrace; // $FlowFixMe It does accept undefined. Error.prepareStackTrace = undefined; var previousDispatcher; { previousDispatcher = ReactCurrentDispatcher$1.current; // Set the dispatcher in DEV because this might be call in the render function // for warnings. ReactCurrentDispatcher$1.current = null; disableLogs(); } try { // This should throw. if (construct) { // Something should be setting the props in the constructor. var Fake = function () { throw Error(); }; // $FlowFixMe Object.defineProperty(Fake.prototype, 'props', { set: function () { // We use a throwing setter instead of frozen or non-writable props // because that won't throw in a non-strict mode function. throw Error(); } }); if (typeof Reflect === 'object' && Reflect.construct) { // We construct a different control for this case to include any extra // frames added by the construct call. try { Reflect.construct(Fake, []); } catch (x) { control = x; } Reflect.construct(fn, [], Fake); } else { try { Fake.call(); } catch (x) { control = x; } fn.call(Fake.prototype); } } else { try { throw Error(); } catch (x) { control = x; } fn(); } } catch (sample) { // This is inlined manually because closure doesn't do it for us. if (sample && control && typeof sample.stack === 'string') { // This extracts the first frame from the sample that isn't also in the control. // Skipping one frame that we assume is the frame that calls the two. var sampleLines = sample.stack.split('\n'); var controlLines = control.stack.split('\n'); var s = sampleLines.length - 1; var c = controlLines.length - 1; while (s >= 1 && c >= 0 && sampleLines[s] !== controlLines[c]) { // We expect at least one stack frame to be shared. // Typically this will be the root most one. However, stack frames may be // cut off due to maximum stack limits. In this case, one maybe cut off // earlier than the other. We assume that the sample is longer or the same // and there for cut off earlier. So we should find the root most frame in // the sample somewhere in the control. c--; } for (; s >= 1 && c >= 0; s--, c--) { // Next we find the first one that isn't the same which should be the // frame that called our sample function and the control. if (sampleLines[s] !== controlLines[c]) { // In V8, the first line is describing the message but other VMs don't. // If we're about to return the first line, and the control is also on the same // line, that's a pretty good indicator that our sample threw at same line as // the control. I.e. before we entered the sample frame. So we ignore this result. // This can happen if you passed a class to function component, or non-function. if (s !== 1 || c !== 1) { do { s--; c--; // We may still have similar intermediate frames from the construct call. // The next one that isn't the same should be our match though. if (c < 0 || sampleLines[s] !== controlLines[c]) { // V8 adds a "new" prefix for native classes. Let's remove it to make it prettier. var _frame = '\n' + sampleLines[s].replace(' at new ', ' at '); // If our component frame is labeled "" // but we have a user-provided "displayName" // splice it in to make the stack more readable. if (fn.displayName && _frame.includes('')) { _frame = _frame.replace('', fn.displayName); } { if (typeof fn === 'function') { componentFrameCache.set(fn, _frame); } } // Return the line we found. return _frame; } } while (s >= 1 && c >= 0); } break; } } } } finally { reentry = false; { ReactCurrentDispatcher$1.current = previousDispatcher; reenableLogs(); } Error.prepareStackTrace = previousPrepareStackTrace; } // Fallback to just using the name if we couldn't make it throw. var name = fn ? fn.displayName || fn.name : ''; var syntheticFrame = name ? describeBuiltInComponentFrame(name) : ''; { if (typeof fn === 'function') { componentFrameCache.set(fn, syntheticFrame); } } return syntheticFrame; } function describeFunctionComponentFrame(fn, source, ownerFn) { { return describeNativeComponentFrame(fn, false); } } function shouldConstruct(Component) { var prototype = Component.prototype; return !!(prototype && prototype.isReactComponent); } function describeUnknownElementTypeFrameInDEV(type, source, ownerFn) { if (type == null) { return ''; } if (typeof type === 'function') { { return describeNativeComponentFrame(type, shouldConstruct(type)); } } if (typeof type === 'string') { return describeBuiltInComponentFrame(type); } switch (type) { case REACT_SUSPENSE_TYPE: return describeBuiltInComponentFrame('Suspense'); case REACT_SUSPENSE_LIST_TYPE: return describeBuiltInComponentFrame('SuspenseList'); } if (typeof type === 'object') { switch (type.$$typeof) { case REACT_FORWARD_REF_TYPE: return describeFunctionComponentFrame(type.render); case REACT_MEMO_TYPE: // Memo may contain any component type so we recursively resolve it. return describeUnknownElementTypeFrameInDEV(type.type, source, ownerFn); case REACT_LAZY_TYPE: { var lazyComponent = type; var payload = lazyComponent._payload; var init = lazyComponent._init; try { // Lazy may contain any component type so we recursively resolve it. return describeUnknownElementTypeFrameInDEV(init(payload), source, ownerFn); } catch (x) {} } } } return ''; } var loggedTypeFailures = {}; var ReactDebugCurrentFrame$1 = ReactSharedInternals.ReactDebugCurrentFrame; function setCurrentlyValidatingElement(element) { { if (element) { var owner = element._owner; var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null); ReactDebugCurrentFrame$1.setExtraStackFrame(stack); } else { ReactDebugCurrentFrame$1.setExtraStackFrame(null); } } } function checkPropTypes(typeSpecs, values, location, componentName, element) { { // $FlowFixMe This is okay but Flow doesn't know it. var has = Function.call.bind(hasOwnProperty); for (var typeSpecName in typeSpecs) { if (has(typeSpecs, typeSpecName)) { var error$1 = void 0; // Prop type validation may throw. In case they do, we don't want to // fail the render phase where it didn't fail before. So we log it. // After these have been cleaned up, we'll let them throw. try { // This is intentionally an invariant that gets caught. It's the same // behavior as without this statement except with a better message. if (typeof typeSpecs[typeSpecName] !== 'function') { // eslint-disable-next-line react-internal/prod-error-codes var err = Error((componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' + 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' + 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.'); err.name = 'Invariant Violation'; throw err; } error$1 = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'); } catch (ex) { error$1 = ex; } if (error$1 && !(error$1 instanceof Error)) { setCurrentlyValidatingElement(element); error('%s: type specification of %s' + ' `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', location, typeSpecName, typeof error$1); setCurrentlyValidatingElement(null); } if (error$1 instanceof Error && !(error$1.message in loggedTypeFailures)) { // Only monitor this failure once because there tends to be a lot of the // same error. loggedTypeFailures[error$1.message] = true; setCurrentlyValidatingElement(element); error('Failed %s type: %s', location, error$1.message); setCurrentlyValidatingElement(null); } } } } } function setCurrentlyValidatingElement$1(element) { { if (element) { var owner = element._owner; var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null); setExtraStackFrame(stack); } else { setExtraStackFrame(null); } } } var propTypesMisspellWarningShown; { propTypesMisspellWarningShown = false; } function getDeclarationErrorAddendum() { if (ReactCurrentOwner.current) { var name = getComponentNameFromType(ReactCurrentOwner.current.type); if (name) { return '\n\nCheck the render method of `' + name + '`.'; } } return ''; } function getSourceInfoErrorAddendum(source) { if (source !== undefined) { var fileName = source.fileName.replace(/^.*[\\\/]/, ''); var lineNumber = source.lineNumber; return '\n\nCheck your code at ' + fileName + ':' + lineNumber + '.'; } return ''; } function getSourceInfoErrorAddendumForProps(elementProps) { if (elementProps !== null && elementProps !== undefined) { return getSourceInfoErrorAddendum(elementProps.__source); } return ''; } /** * Warn if there's no key explicitly set on dynamic arrays of children or * object keys are not valid. This allows us to keep track of children between * updates. */ var ownerHasKeyUseWarning = {}; function getCurrentComponentErrorInfo(parentType) { var info = getDeclarationErrorAddendum(); if (!info) { var parentName = typeof parentType === 'string' ? parentType : parentType.displayName || parentType.name; if (parentName) { info = "\n\nCheck the top-level render call using <" + parentName + ">."; } } return info; } /** * Warn if the element doesn't have an explicit key assigned to it. * This element is in an array. The array could grow and shrink or be * reordered. All children that haven't already been validated are required to * have a "key" property assigned to it. Error statuses are cached so a warning * will only be shown once. * * @internal * @param {ReactElement} element Element that requires a key. * @param {*} parentType element's parent's type. */ function validateExplicitKey(element, parentType) { if (!element._store || element._store.validated || element.key != null) { return; } element._store.validated = true; var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType); if (ownerHasKeyUseWarning[currentComponentErrorInfo]) { return; } ownerHasKeyUseWarning[currentComponentErrorInfo] = true; // Usually the current owner is the offender, but if it accepts children as a // property, it may be the creator of the child that's responsible for // assigning it a key. var childOwner = ''; if (element && element._owner && element._owner !== ReactCurrentOwner.current) { // Give the component that originally created this child. childOwner = " It was passed a child from " + getComponentNameFromType(element._owner.type) + "."; } { setCurrentlyValidatingElement$1(element); error('Each child in a list should have a unique "key" prop.' + '%s%s See https://reactjs.org/link/warning-keys for more information.', currentComponentErrorInfo, childOwner); setCurrentlyValidatingElement$1(null); } } /** * Ensure that every element either is passed in a static location, in an * array with an explicit keys property defined, or in an object literal * with valid key property. * * @internal * @param {ReactNode} node Statically passed child of any type. * @param {*} parentType node's parent's type. */ function validateChildKeys(node, parentType) { if (typeof node !== 'object') { return; } if (isArray(node)) { for (var i = 0; i < node.length; i++) { var child = node[i]; if (isValidElement(child)) { validateExplicitKey(child, parentType); } } } else if (isValidElement(node)) { // This element was passed in a valid location. if (node._store) { node._store.validated = true; } } else if (node) { var iteratorFn = getIteratorFn(node); if (typeof iteratorFn === 'function') { // Entry iterators used to provide implicit keys, // but now we print a separate warning for them later. if (iteratorFn !== node.entries) { var iterator = iteratorFn.call(node); var step; while (!(step = iterator.next()).done) { if (isValidElement(step.value)) { validateExplicitKey(step.value, parentType); } } } } } } /** * Given an element, validate that its props follow the propTypes definition, * provided by the type. * * @param {ReactElement} element */ function validatePropTypes(element) { { var type = element.type; if (type === null || type === undefined || typeof type === 'string') { return; } var propTypes; if (typeof type === 'function') { propTypes = type.propTypes; } else if (typeof type === 'object' && (type.$$typeof === REACT_FORWARD_REF_TYPE || // Note: Memo only checks outer props here. // Inner props are checked in the reconciler. type.$$typeof === REACT_MEMO_TYPE)) { propTypes = type.propTypes; } else { return; } if (propTypes) { // Intentionally inside to avoid triggering lazy initializers: var name = getComponentNameFromType(type); checkPropTypes(propTypes, element.props, 'prop', name, element); } else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) { propTypesMisspellWarningShown = true; // Intentionally inside to avoid triggering lazy initializers: var _name = getComponentNameFromType(type); error('Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?', _name || 'Unknown'); } if (typeof type.getDefaultProps === 'function' && !type.getDefaultProps.isReactClassApproved) { error('getDefaultProps is only used on classic React.createClass ' + 'definitions. Use a static property named `defaultProps` instead.'); } } } /** * Given a fragment, validate that it can only be provided with fragment props * @param {ReactElement} fragment */ function validateFragmentProps(fragment) { { var keys = Object.keys(fragment.props); for (var i = 0; i < keys.length; i++) { var key = keys[i]; if (key !== 'children' && key !== 'key') { setCurrentlyValidatingElement$1(fragment); error('Invalid prop `%s` supplied to `React.Fragment`. ' + 'React.Fragment can only have `key` and `children` props.', key); setCurrentlyValidatingElement$1(null); break; } } if (fragment.ref !== null) { setCurrentlyValidatingElement$1(fragment); error('Invalid attribute `ref` supplied to `React.Fragment`.'); setCurrentlyValidatingElement$1(null); } } } function createElementWithValidation(type, props, children) { var validType = isValidElementType(type); // We warn in this case but don't throw. We expect the element creation to // succeed and there will likely be errors in render. if (!validType) { var info = ''; if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) { info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and named imports."; } var sourceInfo = getSourceInfoErrorAddendumForProps(props); if (sourceInfo) { info += sourceInfo; } else { info += getDeclarationErrorAddendum(); } var typeString; if (type === null) { typeString = 'null'; } else if (isArray(type)) { typeString = 'array'; } else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) { typeString = "<" + (getComponentNameFromType(type.type) || 'Unknown') + " />"; info = ' Did you accidentally export a JSX literal instead of a component?'; } else { typeString = typeof type; } { error('React.createElement: type is invalid -- expected a string (for ' + 'built-in components) or a class/function (for composite ' + 'components) but got: %s.%s', typeString, info); } } var element = createElement.apply(this, arguments); // The result can be nullish if a mock or a custom function is used. // TODO: Drop this when these are no longer allowed as the type argument. if (element == null) { return element; } // Skip key warning if the type isn't valid since our key validation logic // doesn't expect a non-string/function type and can throw confusing errors. // We don't want exception behavior to differ between dev and prod. // (Rendering will throw with a helpful message and as soon as the type is // fixed, the key warnings will appear.) if (validType) { for (var i = 2; i < arguments.length; i++) { validateChildKeys(arguments[i], type); } } if (type === REACT_FRAGMENT_TYPE) { validateFragmentProps(element); } else { validatePropTypes(element); } return element; } var didWarnAboutDeprecatedCreateFactory = false; function createFactoryWithValidation(type) { var validatedFactory = createElementWithValidation.bind(null, type); validatedFactory.type = type; { if (!didWarnAboutDeprecatedCreateFactory) { didWarnAboutDeprecatedCreateFactory = true; warn('React.createFactory() is deprecated and will be removed in ' + 'a future major release. Consider using JSX ' + 'or use React.createElement() directly instead.'); } // Legacy hook: remove it Object.defineProperty(validatedFactory, 'type', { enumerable: false, get: function () { warn('Factory.type is deprecated. Access the class directly ' + 'before passing it to createFactory.'); Object.defineProperty(this, 'type', { value: type }); return type; } }); } return validatedFactory; } function cloneElementWithValidation(element, props, children) { var newElement = cloneElement.apply(this, arguments); for (var i = 2; i < arguments.length; i++) { validateChildKeys(arguments[i], newElement.type); } validatePropTypes(newElement); return newElement; } function startTransition(scope, options) { var prevTransition = ReactCurrentBatchConfig.transition; ReactCurrentBatchConfig.transition = {}; var currentTransition = ReactCurrentBatchConfig.transition; { ReactCurrentBatchConfig.transition._updatedFibers = new Set(); } try { scope(); } finally { ReactCurrentBatchConfig.transition = prevTransition; { if (prevTransition === null && currentTransition._updatedFibers) { var updatedFibersCount = currentTransition._updatedFibers.size; if (updatedFibersCount > 10) { warn('Detected a large number of updates inside startTransition. ' + 'If this is due to a subscription please re-write it to use React provided hooks. ' + 'Otherwise concurrent mode guarantees are off the table.'); } currentTransition._updatedFibers.clear(); } } } } var didWarnAboutMessageChannel = false; var enqueueTaskImpl = null; function enqueueTask(task) { if (enqueueTaskImpl === null) { try { // read require off the module object to get around the bundlers. // we don't want them to detect a require and bundle a Node polyfill. var requireString = ('require' + Math.random()).slice(0, 7); var nodeRequire = module && module[requireString]; // assuming we're in node, let's try to get node's // version of setImmediate, bypassing fake timers if any. enqueueTaskImpl = nodeRequire.call(module, 'timers').setImmediate; } catch (_err) { // we're in a browser // we can't use regular timers because they may still be faked // so we try MessageChannel+postMessage instead enqueueTaskImpl = function (callback) { { if (didWarnAboutMessageChannel === false) { didWarnAboutMessageChannel = true; if (typeof MessageChannel === 'undefined') { error('This browser does not have a MessageChannel implementation, ' + 'so enqueuing tasks via await act(async () => ...) will fail. ' + 'Please file an issue at https://github.com/facebook/react/issues ' + 'if you encounter this warning.'); } } } var channel = new MessageChannel(); channel.port1.onmessage = callback; channel.port2.postMessage(undefined); }; } } return enqueueTaskImpl(task); } var actScopeDepth = 0; var didWarnNoAwaitAct = false; function act(callback) { { // `act` calls can be nested, so we track the depth. This represents the // number of `act` scopes on the stack. var prevActScopeDepth = actScopeDepth; actScopeDepth++; if (ReactCurrentActQueue.current === null) { // This is the outermost `act` scope. Initialize the queue. The reconciler // will detect the queue and use it instead of Scheduler. ReactCurrentActQueue.current = []; } var prevIsBatchingLegacy = ReactCurrentActQueue.isBatchingLegacy; var result; try { // Used to reproduce behavior of `batchedUpdates` in legacy mode. Only // set to `true` while the given callback is executed, not for updates // triggered during an async event, because this is how the legacy // implementation of `act` behaved. ReactCurrentActQueue.isBatchingLegacy = true; result = callback(); // Replicate behavior of original `act` implementation in legacy mode, // which flushed updates immediately after the scope function exits, even // if it's an async function. if (!prevIsBatchingLegacy && ReactCurrentActQueue.didScheduleLegacyUpdate) { var queue = ReactCurrentActQueue.current; if (queue !== null) { ReactCurrentActQueue.didScheduleLegacyUpdate = false; flushActQueue(queue); } } } catch (error) { popActScope(prevActScopeDepth); throw error; } finally { ReactCurrentActQueue.isBatchingLegacy = prevIsBatchingLegacy; } if (result !== null && typeof result === 'object' && typeof result.then === 'function') { var thenableResult = result; // The callback is an async function (i.e. returned a promise). Wait // for it to resolve before exiting the current scope. var wasAwaited = false; var thenable = { then: function (resolve, reject) { wasAwaited = true; thenableResult.then(function (returnValue) { popActScope(prevActScopeDepth); if (actScopeDepth === 0) { // We've exited the outermost act scope. Recursively flush the // queue until there's no remaining work. recursivelyFlushAsyncActWork(returnValue, resolve, reject); } else { resolve(returnValue); } }, function (error) { // The callback threw an error. popActScope(prevActScopeDepth); reject(error); }); } }; { if (!didWarnNoAwaitAct && typeof Promise !== 'undefined') { // eslint-disable-next-line no-undef Promise.resolve().then(function () {}).then(function () { if (!wasAwaited) { didWarnNoAwaitAct = true; error('You called act(async () => ...) without await. ' + 'This could lead to unexpected testing behaviour, ' + 'interleaving multiple act calls and mixing their ' + 'scopes. ' + 'You should - await act(async () => ...);'); } }); } } return thenable; } else { var returnValue = result; // The callback is not an async function. Exit the current scope // immediately, without awaiting. popActScope(prevActScopeDepth); if (actScopeDepth === 0) { // Exiting the outermost act scope. Flush the queue. var _queue = ReactCurrentActQueue.current; if (_queue !== null) { flushActQueue(_queue); ReactCurrentActQueue.current = null; } // Return a thenable. If the user awaits it, we'll flush again in // case additional work was scheduled by a microtask. var _thenable = { then: function (resolve, reject) { // Confirm we haven't re-entered another `act` scope, in case // the user does something weird like await the thenable // multiple times. if (ReactCurrentActQueue.current === null) { // Recursively flush the queue until there's no remaining work. ReactCurrentActQueue.current = []; recursivelyFlushAsyncActWork(returnValue, resolve, reject); } else { resolve(returnValue); } } }; return _thenable; } else { // Since we're inside a nested `act` scope, the returned thenable // immediately resolves. The outer scope will flush the queue. var _thenable2 = { then: function (resolve, reject) { resolve(returnValue); } }; return _thenable2; } } } } function popActScope(prevActScopeDepth) { { if (prevActScopeDepth !== actScopeDepth - 1) { error('You seem to have overlapping act() calls, this is not supported. ' + 'Be sure to await previous act() calls before making a new one. '); } actScopeDepth = prevActScopeDepth; } } function recursivelyFlushAsyncActWork(returnValue, resolve, reject) { { var queue = ReactCurrentActQueue.current; if (queue !== null) { try { flushActQueue(queue); enqueueTask(function () { if (queue.length === 0) { // No additional work was scheduled. Finish. ReactCurrentActQueue.current = null; resolve(returnValue); } else { // Keep flushing work until there's none left. recursivelyFlushAsyncActWork(returnValue, resolve, reject); } }); } catch (error) { reject(error); } } else { resolve(returnValue); } } } var isFlushing = false; function flushActQueue(queue) { { if (!isFlushing) { // Prevent re-entrance. isFlushing = true; var i = 0; try { for (; i < queue.length; i++) { var callback = queue[i]; do { callback = callback(true); } while (callback !== null); } queue.length = 0; } catch (error) { // If something throws, leave the remaining callbacks on the queue. queue = queue.slice(i + 1); throw error; } finally { isFlushing = false; } } } } var createElement$1 = createElementWithValidation ; var cloneElement$1 = cloneElementWithValidation ; var createFactory = createFactoryWithValidation ; var Children = { map: mapChildren, forEach: forEachChildren, count: countChildren, toArray: toArray, only: onlyChild }; exports.Children = Children; exports.Component = Component; exports.Fragment = REACT_FRAGMENT_TYPE; exports.Profiler = REACT_PROFILER_TYPE; exports.PureComponent = PureComponent; exports.StrictMode = REACT_STRICT_MODE_TYPE; exports.Suspense = REACT_SUSPENSE_TYPE; exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = ReactSharedInternals; exports.cloneElement = cloneElement$1; exports.createContext = createContext; exports.createElement = createElement$1; exports.createFactory = createFactory; exports.createRef = createRef; exports.forwardRef = forwardRef; exports.isValidElement = isValidElement; exports.lazy = lazy; exports.memo = memo; exports.startTransition = startTransition; exports.unstable_act = act; exports.useCallback = useCallback; exports.useContext = useContext; exports.useDebugValue = useDebugValue; exports.useDeferredValue = useDeferredValue; exports.useEffect = useEffect; exports.useId = useId; exports.useImperativeHandle = useImperativeHandle; exports.useInsertionEffect = useInsertionEffect; exports.useLayoutEffect = useLayoutEffect; exports.useMemo = useMemo; exports.useReducer = useReducer; exports.useRef = useRef; exports.useState = useState; exports.useSyncExternalStore = useSyncExternalStore; exports.useTransition = useTransition; exports.version = ReactVersion; /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ if ( typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' && typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop === 'function' ) { __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(new Error()); } })(); } }).call(this,require("hmr7eR")) },{"hmr7eR":1}],13:[function(require,module,exports){ /** * @license React * react.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 'use strict';var l=Symbol.for("react.element"),n=Symbol.for("react.portal"),p=Symbol.for("react.fragment"),q=Symbol.for("react.strict_mode"),r=Symbol.for("react.profiler"),t=Symbol.for("react.provider"),u=Symbol.for("react.context"),v=Symbol.for("react.forward_ref"),w=Symbol.for("react.suspense"),x=Symbol.for("react.memo"),y=Symbol.for("react.lazy"),z=Symbol.iterator;function A(a){if(null===a||"object"!==typeof a)return null;a=z&&a[z]||a["@@iterator"];return"function"===typeof a?a:null} var B={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},C=Object.assign,D={};function E(a,b,e){this.props=a;this.context=b;this.refs=D;this.updater=e||B}E.prototype.isReactComponent={}; E.prototype.setState=function(a,b){if("object"!==typeof a&&"function"!==typeof a&&null!=a)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,a,b,"setState")};E.prototype.forceUpdate=function(a){this.updater.enqueueForceUpdate(this,a,"forceUpdate")};function F(){}F.prototype=E.prototype;function G(a,b,e){this.props=a;this.context=b;this.refs=D;this.updater=e||B}var H=G.prototype=new F; H.constructor=G;C(H,E.prototype);H.isPureReactComponent=!0;var I=Array.isArray,J=Object.prototype.hasOwnProperty,K={current:null},L={key:!0,ref:!0,__self:!0,__source:!0}; function M(a,b,e){var d,c={},k=null,h=null;if(null!=b)for(d in void 0!==b.ref&&(h=b.ref),void 0!==b.key&&(k=""+b.key),b)J.call(b,d)&&!L.hasOwnProperty(d)&&(c[d]=b[d]);var g=arguments.length-2;if(1===g)c.children=e;else if(1 import('./MyComponent'))\n\nDid you accidentally put curly braces around the import?",t),"default"in t||w("lazy: Expected the result of a dynamic import() call. Instead received: %s\n\nYour code should look like: \n const MyComponent = lazy(() => import('./MyComponent'))",t),t.default;throw r._result}function Ee(e){return"string"==typeof e||"function"==typeof e||!!(e===f||e===L||K||e===o||e===i||e===F||J||e===V||Y||H||G)||"object"==typeof e&&null!==e&&(e.$$typeof===u||e.$$typeof===y||e.$$typeof===A||e.$$typeof===a||e.$$typeof===d||e.$$typeof===Ie||void 0!==e.getModuleId)}function R(){var e=z.current;return null===e&&w("Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem."),e}var ke,Re,Ce,Pe,xe,Te,$e,Ie=Symbol.for("react.module.reference"),C=0;function Ne(){}var De,Me=l.ReactCurrentDispatcher;function P(e){if(void 0===De)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);De=t&&t[1]||""}return"\n"+De+e}var Le=!(Ne.__reactDisabledLog=!0),Ae=new("function"==typeof WeakMap?WeakMap:Map);function Fe(t,e){if(!t||Le)return"";var r,n=Ae.get(t);if(void 0!==n)return n;Le=!0;var o,a,n=Error.prepareStackTrace;Error.prepareStackTrace=void 0,o=Me.current,Me.current=null,0===C&&(ke=console.log,Re=console.info,Ce=console.warn,Pe=console.error,xe=console.group,Te=console.groupCollapsed,$e=console.groupEnd,a={configurable:!0,enumerable:!0,value:Ne,writable:!0},Object.defineProperties(console,{info:a,log:a,warn:a,error:a,group:a,groupCollapsed:a,groupEnd:a})),C++;try{if(e){var i=function(){throw Error()};if(Object.defineProperty(i.prototype,"props",{set:function(){throw Error()}}),"object"==typeof Reflect&&Reflect.construct){try{Reflect.construct(i,[])}catch(e){r=e}Reflect.construct(t,[],i)}else{try{i.call()}catch(e){r=e}t.call(i.prototype)}}else{try{throw Error()}catch(e){r=e}t()}}catch(e){if(e&&r&&"string"==typeof e.stack){for(var u,c=e.stack.split("\n"),s=r.stack.split("\n"),l=c.length-1,f=s.length-1;1<=l&&0<=f&&c[l]!==s[f];)f--;for(;1<=l&&0<=f;l--,f--)if(c[l]!==s[f]){if(1!==l||1!==f)do{if(l--,--f<0||c[l]!==s[f])return u="\n"+c[l].replace(" at new "," at "),t.displayName&&u.includes("")&&(u=u.replace("",t.displayName)),"function"==typeof t&&Ae.set(t,u),u}while(1<=l&&0<=f);break}}}finally{Le=!1,Me.current=o,0===--C&&(a={configurable:!0,enumerable:!0,writable:!0},Object.defineProperties(console,{log:m({},a,{value:ke}),info:m({},a,{value:Re}),warn:m({},a,{value:Ce}),error:m({},a,{value:Pe}),group:m({},a,{value:xe}),groupCollapsed:m({},a,{value:Te}),groupEnd:m({},a,{value:$e})})),C<0&&w("disabledDepth fell below zero. This is a bug in React. Please file an issue."),Error.prepareStackTrace=n}e=t?t.displayName||t.name:"",i=e?P(e):"";return"function"==typeof t&&Ae.set(t,i),i}function x(e,t,r){if(null!=e){if("function"==typeof e)return Fe(e,!(!(n=(n=e).prototype)||!n.isReactComponent));var n;if("string"==typeof e)return P(e);switch(e){case i:return P("Suspense");case F:return P("SuspenseList")}if("object"==typeof e)switch(e.$$typeof){case d:return Fe(e.render,!1);case y:return x(e.type,t,r);case u:var o=e._payload,a=e._init;try{return x(a(o),t,r)}catch(e){}}}return""}var Ve={},Ue=l.ReactDebugCurrentFrame;function T(e){var t;e?(t=e._owner,e=x(e.type,e._source,t?t.type:null),Ue.setExtraStackFrame(e)):Ue.setExtraStackFrame(null)}function $(e){var t;e?(t=e._owner,B(x(e.type,e._source,t?t.type:null))):B(null)}function We(){if(g.current){var e=O(g.current.type);if(e)return"\n\nCheck the render method of `"+e+"`."}return""}var qe=!1,ze={};function Be(e,t){var r;!e._store||e._store.validated||null!=e.key||(e._store.validated=!0,t=t,(r=We())||(t="string"==typeof t?t:t.displayName||t.name)&&(r="\n\nCheck the top-level render call using <"+t+">."),ze[t=r])||(ze[t]=!0,r="",e&&e._owner&&e._owner!==g.current&&(r=" It was passed a child from "+O(e._owner.type)+"."),$(e),w('Each child in a list should have a unique "key" prop.%s%s See https://reactjs.org/link/warning-keys for more information.',t,r),$(null))}function Ye(e,t){if("object"==typeof e)if(S(e))for(var r=0;r",n=" Did you accidentally export a JSX literal instead of a component?"):o=typeof e,w("React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",o,n)),ye.apply(this,arguments));if(null!=t){if(a)for(var i=2;i ...) will fail. Please file an issue at https://github.com/facebook/react/issues if you encounter this warning.");var t=new MessageChannel;t.port1.onmessage=e,t.port2.postMessage(void 0)}}I(o)}catch(e){r(e)}else t(e)}var Ze=!1;function et(t){if(!Ze){Ze=!0;var r=0;try{for(;r is not supported and will be removed in a future major release. Did you mean to render instead?")),t.Provider},set:function(e){t.Provider=e}},_currentValue:{get:function(){return t._currentValue},set:function(e){t._currentValue=e}},_currentValue2:{get:function(){return t._currentValue2},set:function(e){t._currentValue2=e}},_threadCount:{get:function(){return t._threadCount},set:function(e){t._threadCount=e}},Consumer:{get:function(){return r||(r=!0,w("Rendering is not supported and will be removed in a future major release. Did you mean to render instead?")),t.Consumer}},displayName:{get:function(){return t.displayName},set:function(e){o||(_("Setting `displayName` on Context.Consumer has no effect. You should set it directly on the context with Context.displayName = '%s'.",e),o=!0)}}}),t.Consumer=e,t._currentRenderer=null,t._currentRenderer2=null,t},ot.createElement=e,ot.createFactory=rt,ot.createRef=function(){var e={current:null};return Object.seal(e),e},ot.forwardRef=function(t){null!=t&&t.$$typeof===y?w("forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...))."):"function"!=typeof t?w("forwardRef requires a render function but was given %s.",null===t?"null":typeof t):0!==t.length&&2!==t.length&&w("forwardRef render functions accept exactly two parameters: props and ref. %s",1===t.length?"Did you forget to use the ref parameter?":"Any additional parameter will be undefined."),null==t||null==t.defaultProps&&null==t.propTypes||w("forwardRef render functions do not support propTypes or defaultProps. Did you accidentally pass a React component?");var r,e={$$typeof:d,render:t};return Object.defineProperty(e,"displayName",{enumerable:!1,configurable:!0,get:function(){return r},set:function(e){r=e,t.name||t.displayName||(t.displayName=e)}}),e},ot.isValidElement=E,ot.lazy=function(e){var t,r,n={$$typeof:u,_payload:{_status:b,_result:e},_init:je};return Object.defineProperties(n,{defaultProps:{configurable:!0,get:function(){return t},set:function(e){w("React.lazy(...): It is not supported to assign `defaultProps` to a lazy component import. Either specify them where the component is defined, or create a wrapping component around it."),t=e,Object.defineProperty(n,"defaultProps",{enumerable:!0})}},propTypes:{configurable:!0,get:function(){return r},set:function(e){w("React.lazy(...): It is not supported to assign `propTypes` to a lazy component import. Either specify them where the component is defined, or create a wrapping component around it."),r=e,Object.defineProperty(n,"propTypes",{enumerable:!0})}}}),n},ot.memo=function(t,e){Ee(t)||w("memo: The first argument must be a component. Instead received: %s",null===t?"null":typeof t);var r,e={$$typeof:y,type:t,compare:void 0===e?null:e};return Object.defineProperty(e,"displayName",{enumerable:!1,configurable:!0,get:function(){return r},set:function(e){r=e,t.name||t.displayName||(t.displayName=e)}}),e},ot.startTransition=function(e,t){var r=c.transition,n=(c.transition={},c.transition);c.transition._updatedFibers=new Set;try{e()}finally{null===(c.transition=r)&&n._updatedFibers&&(10 ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"))}),e):(r=t,D(i),0===N?(null!==(a=s.current)&&(et(a),s.current=null),{then:function(e,t){null===s.current?(s.current=[],Xe(r,e,t)):e(r)}}):{then:function(e,t){e(r)}})},ot.useCallback=function(e,t){return R().useCallback(e,t)},ot.useContext=function(e){var t,r=R();return void 0!==e._context&&((t=e._context).Consumer===e?w("Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be removed in a future major release. Did you mean to call useContext(Context) instead?"):t.Provider===e&&w("Calling useContext(Context.Provider) is not supported. Did you mean to call useContext(Context) instead?")),r.useContext(e)},ot.useDebugValue=function(e,t){return R().useDebugValue(e,t)},ot.useDeferredValue=function(e){return R().useDeferredValue(e)},ot.useEffect=function(e,t){return R().useEffect(e,t)},ot.useId=function(){return R().useId()},ot.useImperativeHandle=function(e,t,r){return R().useImperativeHandle(e,t,r)},ot.useInsertionEffect=function(e,t){return R().useInsertionEffect(e,t)},ot.useLayoutEffect=function(e,t){return R().useLayoutEffect(e,t)},ot.useMemo=function(e,t){return R().useMemo(e,t)},ot.useReducer=function(e,t,r){return R().useReducer(e,t,r)},ot.useRef=function(e){return R().useRef(e)},ot.useState=function(e){return R().useState(e)},ot.useSyncExternalStore=function(e,t,r){return R().useSyncExternalStore(e,t,r)},ot.useTransition=function(){return R().useTransition()},ot.version="18.2.0","undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(new Error)}}.call(this,e("hmr7eR"))},{hmr7eR:1}],13:[function(e,t,r){"use strict";var f=Symbol.for("react.element"),p=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),o=Symbol.for("react.strict_mode"),a=Symbol.for("react.profiler"),i=Symbol.for("react.provider"),u=Symbol.for("react.context"),c=Symbol.for("react.forward_ref"),s=Symbol.for("react.suspense"),l=Symbol.for("react.memo"),d=Symbol.for("react.lazy"),y=Symbol.iterator;var m={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},h=Object.assign,b={};function v(e,t,r){this.props=e,this.context=t,this.refs=b,this.updater=r||m}function g(){}function _(e,t,r){this.props=e,this.context=t,this.refs=b,this.updater=r||m}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},g.prototype=v.prototype;var w=_.prototype=new g,S=(w.constructor=_,h(w,v.prototype),w.isPureReactComponent=!0,Array.isArray),O=Object.prototype.hasOwnProperty,j={current:null},E={key:!0,ref:!0,__self:!0,__source:!0};function k(e,t,r){var n,o={},a=null,i=null;if(null!=t)for(n in void 0!==t.ref&&(i=t.ref),void 0!==t.key&&(a=""+t.key),t)O.call(t,n)&&!E.hasOwnProperty(n)&&(o[n]=t[n]);var u=arguments.length-2;if(1===u)o.children=r;else if(1 res.json() ) .then( ( result ) => { this.setState( { isLoading: false, form: result.data, } ); }, ( error ) => { this.setState( { isLoading: false, error, } ); }, ); } /** * Render module view. * * @since 1.6.3 * * @returns {JSX.Element} View for module. */ render() { const { error, isLoaded, form } = this.state, wrapperClasses = isLoaded ? 'wpforms-divi-form-preview loading' : 'wpforms-divi-form-preview'; if ( typeof this.props.form_id === 'undefined' || this.props.form_id === '' ) { return (
    {

    }

    { wpforms_divi_builder.help_text }  { window.open( wpforms_divi_builder.guide_url, '_blank' ); } } > { wpforms_divi_builder.guide_text }.

    ); } if ( error || ! form ) { return (
    ); } return (
    {
    }
    ); } } jQuery( window ) // Register custom modules. .on( 'et_builder_api_ready', ( event, API ) => { API.registerModules( [ WPFormsSelector ] ); } ) // Re-initialize WPForms frontend. .on( 'wpformsDiviModuleDisplay', () => { window.wpforms.init(); } ); jQuery( document ) .on( 'wpformsReady', function() { const $ = jQuery; // Make all the modern dropdowns disabled. $( '.choicesjs-select' ).each( function() { const $instance = $( this ).data( 'choicesjs' ); if ( $instance && typeof $instance.disable === 'function' ) { $instance.disable(); } } ); // Init Repeater fields. if ( 'undefined' !== typeof WPFormsRepeaterField ) { WPFormsRepeaterField.ready(); } } ); integrations/elementor/editor-modern.js000064400000033234151716470030014363 0ustar00/* global elementor, elementorCommon, wpformsElementorVars, elementorFrontend, Choices */ 'use strict'; /** * WPForms integration with Elementor (modern widget). * * @since 1.8.3 */ var WPFormsElementorModern = window.WPFormsElementorModern || ( function( document, window, $ ) { /** * Public functions and properties. * * @since 1.8.3 * * @type {object} */ var app = { /** * Start the engine. * * @since 1.8.3 */ init: function() { app.events(); }, /** * Register JS events. * * @since 1.8.3 */ events: function() { // Widget events. $( window ) .on( 'elementor/frontend/init', function( event, id, instance ) { elementor.channels.editor.on( 'elementorWPFormsResetStyleSettings', app.confirmResetStyleSettings ); elementor.channels.editor.on( 'section:activated', app.checkForLeadForms ); elementor.hooks.addAction( 'panel/open_editor/widget/wpforms', app.widgetPanelOpen ); elementorFrontend.hooks.addAction( 'frontend/element_ready/wpforms.default', app.widgetReady ); } ); }, /** * On section change event handler. * * @since 1.8.3 * * @param {string} sectionName The current section name. * @param {object} editor Editor instance. * */ checkForLeadForms( sectionName, editor ) { if ( sectionName !== 'field_styles' || editor.model.attributes.widgetType !== 'wpforms' ) { return; } let $panelContent = editor.$childViewContainer[0]; let widgetView = editor.options.editedElementView.$el[0]; let formId = editor.model.attributes.settings.attributes.form_id; let $form = $( widgetView ).find( `#wpforms-${formId}` ); if ( $form.length === 0 ) { return; } if ( $form.hasClass( 'wpforms-lead-forms-container' ) ) { $( $panelContent ).addClass( 'wpforms-elementor-disabled' ); $( $panelContent ).find( '.wpforms-elementor-lead-forms-notice' ).css( 'display', 'block' ); } }, /** * Initialize widget controls when widget is activated. * * @since 1.8.3 * * @param {object} panel Panel object. * @param {object} model Model object. * @param {object} view View object. */ widgetPanelOpen: function( panel, model, view ) { const settingsModel = model.get( 'settings' ); // Apply settings from the textarea. settingsModel.on( 'change:copyPasteJsonValue', ( changedModel ) => { app.pasteSettings( changedModel, view ); } ); // Change style settings. settingsModel.on( 'change', ( changedModel ) => { app.changeStyleSettings( changedModel, view ); if ( ! changedModel.changed.copyPasteJsonValue && ! changedModel.changed.form_id ) { app.updateCopyPasteContent( changedModel ); } } ); // Update copy/paste content when form_id is changed and copyPasteJsonValue is not set. settingsModel.on( 'change:form_id', ( changedModel ) => { if ( ! changedModel.attributes.copyPasteJsonValue ) { setTimeout( function() { app.updateCopyPasteContent( changedModel ); }, 0 ); } } ); }, /** * Widget ready events. * * @since 1.8.3 * * @param {jQuery} $scope The current element wrapped with jQuery. */ widgetReady: function( $scope ) { let formId = $scope.find( '.wpforms-form' ).data( 'formid' ); app.updateAccentColors( $scope, formId ); app.loadChoicesJS( $scope, formId ); app.initRichTextField( formId ); app.initRepeaterField( formId ); }, /** * Show the reset style settings confirm popup. * * @since 1.8.3 * * @param {object} event Event object. */ confirmResetStyleSettings: function( event ) { elementorCommon.dialogsManager.createWidget( 'confirm', { message: wpformsElementorVars.strings.reset_settings_confirm_text, headerMessage: wpformsElementorVars.strings.reset_style_settings, strings: { confirm: wpformsElementorVars.strings.continue, cancel: wpformsElementorVars.strings.cancel, }, defaultOption: 'cancel', onConfirm: function onConfirm() { app.resetStyleSettings( event ); }, } ).show(); }, /** * Reset style settings button handler. * * @since 1.8.3 * * @param {Object} event Event object. */ resetStyleSettings( event ) { const model = event.options.elementSettingsModel; const container = event.options.container; const widgetContainer = container.view.$el[ 0 ]; const defaults = model.defaults; const styleSettings = app.getStyleAttributesKeys(); const defaultValues = {}; const $widgetStyles = $( widgetContainer ).find( '#wpforms-css-vars-root' ).next( 'style' ); // Prepare default style settings values. styleSettings.forEach( function( element ) { defaultValues[ element ] = defaults[element]; } ); // Reset global style settings. app.resetGlobalStyleSettings( model, container ); // Reset widget settings to default. elementorCommon.api.run( 'document/elements/settings', { container, options: { external: true, }, settings: defaultValues, } ); // Reset CSS vars for widget container and form specific { if ( event.key === 'Enter' || event.key === ' ' ) { onPreviewClicked(); } } } >
    ); }; BackgroundPreview.propTypes = { attributes: PropTypes.object.isRequired, onRemoveBackground: PropTypes.func.isRequired, onPreviewClicked: PropTypes.func.isRequired, }; export default BackgroundPreview; integrations/gutenberg/modules/background-styles.js000064400000045747151716470030016727 0ustar00/* global wpforms_gutenberg_form_selector */ /* jshint es3: false, esversion: 6 */ import BackgroundPreview from './background-preview.js'; /** * @param strings.background_styles * @param strings.bottom_center * @param strings.bottom_left * @param strings.bottom_right * @param strings.center_center * @param strings.center_left * @param strings.center_right * @param strings.choose_image * @param strings.image_url * @param strings.media_library * @param strings.no_repeat * @param strings.repeat_x * @param strings.repeat_y * @param strings.select_background_image * @param strings.select_image * @param strings.stock_photo * @param strings.tile * @param strings.top_center * @param strings.top_left * @param strings.top_right */ /** * Gutenberg editor block. * * Background styles panel module. * * @since 1.8.8 */ export default ( function() { /** * WP core components. * * @since 1.8.8 */ const { PanelColorSettings } = wp.blockEditor || wp.editor; const { SelectControl, PanelBody, Flex, FlexBlock, __experimentalUnitControl, TextControl, Button } = wp.components; /** * Localized data aliases. * * @since 1.8.8 */ const { strings, defaults } = wpforms_gutenberg_form_selector; /** * Public functions and properties. * * @since 1.8.8 * * @type {Object} */ const app = { /** * Get block attributes. * * @since 1.8.8 * * @return {Object} Block attributes. */ getBlockAttributes() { return { backgroundImage: { type: 'string', default: defaults.backgroundImage, }, backgroundPosition: { type: 'string', default: defaults.backgroundPosition, }, backgroundRepeat: { type: 'string', default: defaults.backgroundRepeat, }, backgroundSizeMode: { type: 'string', default: defaults.backgroundSizeMode, }, backgroundSize: { type: 'string', default: defaults.backgroundSize, }, backgroundWidth: { type: 'string', default: defaults.backgroundWidth, }, backgroundHeight: { type: 'string', default: defaults.backgroundHeight, }, backgroundColor: { type: 'string', default: defaults.backgroundColor, }, backgroundUrl: { type: 'string', default: defaults.backgroundUrl, }, }; }, /** * Get Background Styles panel JSX code. * * @since 1.8.8 * * @param {Object} props Block properties. * @param {Object} handlers Block handlers. * @param {Object} formSelectorCommon Block properties. * @param {Object} stockPhotos Stock Photos module. * @param {Object} uiState UI state. * * @return {Object} Field styles JSX code. */ getBackgroundStyles( props, handlers, formSelectorCommon, stockPhotos, uiState ) { // eslint-disable-line max-lines-per-function, complexity const isNotDisabled = uiState.isNotDisabled; const isProEnabled = uiState.isProEnabled; const showBackgroundPreview = uiState.showBackgroundPreview; const setShowBackgroundPreview = uiState.setShowBackgroundPreview; const lastBgImage = uiState.lastBgImage; const setLastBgImage = uiState.setLastBgImage; const tabIndex = isNotDisabled ? 0 : -1; const cssClass = formSelectorCommon.getPanelClass( props ) + ( isNotDisabled ? '' : ' wpforms-gutenberg-panel-disabled' ); return (
    { if ( isNotDisabled ) { return; } event.stopPropagation(); if ( ! isProEnabled ) { return formSelectorCommon.education.showProModal( 'background', strings.background_styles ); } formSelectorCommon.education.showLicenseModal( 'background', strings.background_styles, 'background-styles' ); } } onKeyDown={ ( event ) => { if ( isNotDisabled ) { return; } event.stopPropagation(); if ( ! isProEnabled ) { return formSelectorCommon.education.showProModal( 'background', strings.background_styles ); } formSelectorCommon.education.showLicenseModal( 'background', strings.background_styles, 'background-styles' ); } } > app.setContainerBackgroundImageWrapper( props, handlers, value, lastBgImage, setLastBgImage ) } /> { ( props.attributes.backgroundImage !== 'none' || ! isNotDisabled ) && ( handlers.styleAttrChange( 'backgroundPosition', value ) } /> ) } { ( props.attributes.backgroundImage !== 'none' || ! isNotDisabled ) && ( handlers.styleAttrChange( 'backgroundRepeat', value ) } /> app.handleSizeFromDimensions( props, handlers, value ) } /> ) } { ( ( props.attributes.backgroundSizeMode === 'dimensions' && props.attributes.backgroundImage !== 'none' ) || ! isNotDisabled ) && ( <__experimentalUnitControl label={ strings.width } tabIndex={ tabIndex } value={ props.attributes.backgroundWidth } isUnitSelectTabbable={ isNotDisabled } onChange={ ( value ) => app.handleSizeFromWidth( props, handlers, value ) } /> <__experimentalUnitControl label={ strings.height } tabIndex={ tabIndex } value={ props.attributes.backgroundHeight } isUnitSelectTabbable={ isNotDisabled } onChange={ ( value ) => app.handleSizeFromHeight( props, handlers, value ) } /> ) } { ( ! showBackgroundPreview || props.attributes.backgroundUrl === 'url()' ) && ( ( props.attributes.backgroundImage === 'library' && ( ) ) || ( props.attributes.backgroundImage === 'stock' && ( ) ) ) } { ( ( showBackgroundPreview && props.attributes.backgroundImage !== 'none' ) || props.attributes.backgroundUrl !== 'url()' ) && (
    { app.onRemoveBackground( setShowBackgroundPreview, handlers, setLastBgImage ); } } onPreviewClicked={ () => { if ( props.attributes.backgroundImage === 'library' ) { return app.openMediaLibrary( props, handlers, setShowBackgroundPreview ); } return stockPhotos?.openModal( props, handlers, 'bg-styles', setShowBackgroundPreview ); } } />
    handlers.styleAttrChange( 'backgroundUrl', value ) } onLoad={ ( value ) => props.attributes.backgroundImage !== 'none' && handlers.styleAttrChange( 'backgroundUrl', value ) } />
    ) }
    { strings.colors }
    { if ( ! isNotDisabled ) { return; } handlers.styleAttrChange( 'backgroundColor', value ); }, label: strings.background, }, ] } />
    ); }, /** * Open media library modal and handle image selection. * * @since 1.8.8 * * @param {Object} props Block properties. * @param {Object} handlers Block handlers. * @param {Function} setShowBackgroundPreview Set show background preview. */ openMediaLibrary( props, handlers, setShowBackgroundPreview ) { const frame = wp.media( { title: strings.select_background_image, multiple: false, library: { type: 'image', }, button: { text: strings.select_image, }, } ); frame.on( 'select', () => { const attachment = frame.state().get( 'selection' ).first().toJSON(); const setAttr = {}; const attribute = 'backgroundUrl'; if ( attachment.url ) { const value = `url(${ attachment.url })`; setAttr[ attribute ] = value; props.setAttributes( setAttr ); handlers.styleAttrChange( 'backgroundUrl', value ); setShowBackgroundPreview( true ); } } ); frame.open(); }, /** * Set container background image. * * @since 1.8.8 * * @param {HTMLElement} container Container element. * @param {string} value Value. * * @return {boolean} True if the value was set, false otherwise. */ setContainerBackgroundImage( container, value ) { if ( value === 'none' ) { container.style.setProperty( `--wpforms-background-url`, 'url()' ); } return true; }, /** * Set container background image. * * @since 1.8.8 * * @param {Object} props Block properties. * @param {Object} handlers Block event handlers. * @param {string} value Value. * @param {string} lastBgImage Last background image. * @param {Function} setLastBgImage Set last background image. */ setContainerBackgroundImageWrapper( props, handlers, value, lastBgImage, setLastBgImage ) { if ( value === 'none' ) { setLastBgImage( props.attributes.backgroundUrl ); props.attributes.backgroundUrl = 'url()'; handlers.styleAttrChange( 'backgroundUrl', 'url()' ); } else if ( lastBgImage ) { props.attributes.backgroundUrl = lastBgImage; handlers.styleAttrChange( 'backgroundUrl', lastBgImage ); } handlers.styleAttrChange( 'backgroundImage', value ); }, /** * Set container background position. * * @since 1.8.8 * * @param {HTMLElement} container Container element. * @param {string} value Value. * * @return {boolean} True if the value was set, false otherwise. */ setContainerBackgroundPosition( container, value ) { container.style.setProperty( `--wpforms-background-position`, value ); return true; }, /** * Set container background repeat. * * @since 1.8.8 * * @param {HTMLElement} container Container element. * @param {string} value Value. * * @return {boolean} True if the value was set, false otherwise. */ setContainerBackgroundRepeat( container, value ) { container.style.setProperty( `--wpforms-background-repeat`, value ); return true; }, /** * Handle real size from dimensions. * * @since 1.8.8 * * @param {Object} props Block properties. * @param {Object} handlers Block handlers. * @param {string} value Value. */ handleSizeFromDimensions( props, handlers, value ) { if ( value === 'cover' ) { props.attributes.backgroundSize = 'cover'; handlers.styleAttrChange( 'backgroundWidth', props.attributes.backgroundWidth ); handlers.styleAttrChange( 'backgroundHeight', props.attributes.backgroundHeight ); handlers.styleAttrChange( 'backgroundSizeMode', 'cover' ); handlers.styleAttrChange( 'backgroundSize', 'cover' ); } else { props.attributes.backgroundSize = 'dimensions'; handlers.styleAttrChange( 'backgroundSizeMode', 'dimensions' ); handlers.styleAttrChange( 'backgroundSize', props.attributes.backgroundWidth + ' ' + props.attributes.backgroundHeight ); } }, /** * Handle real size from width. * * @since 1.8.8 * * @param {Object} props Block properties. * @param {Object} handlers Block handlers. * @param {string} value Value. */ handleSizeFromWidth( props, handlers, value ) { props.attributes.backgroundSize = value + ' ' + props.attributes.backgroundHeight; props.attributes.backgroundWidth = value; handlers.styleAttrChange( 'backgroundSize', value + ' ' + props.attributes.backgroundHeight ); handlers.styleAttrChange( 'backgroundWidth', value ); }, /** * Handle real size from height. * * @since 1.8.8 * * @param {Object} props Block properties. * @param {Object} handlers Block handlers. * @param {string} value Value. */ handleSizeFromHeight( props, handlers, value ) { props.attributes.backgroundSize = props.attributes.backgroundWidth + ' ' + value; props.attributes.backgroundHeight = value; handlers.styleAttrChange( 'backgroundSize', props.attributes.backgroundWidth + ' ' + value ); handlers.styleAttrChange( 'backgroundHeight', value ); }, /** * Set container background width. * * @since 1.8.8 * * @param {HTMLElement} container Container element. * @param {string} value Value. * * @return {boolean} True if the value was set, false otherwise. */ setContainerBackgroundWidth( container, value ) { container.style.setProperty( `--wpforms-background-width`, value ); return true; }, /** * Set container background height. * * @since 1.8.8 * * @param {HTMLElement} container Container element. * @param {string} value Value. * * @return {boolean} True if the value was set, false otherwise. */ setContainerBackgroundHeight( container, value ) { container.style.setProperty( `--wpforms-background-height`, value ); return true; }, /** * Set container background url. * * @since 1.8.8 * * @param {HTMLElement} container Container element. * @param {string} value Value. * * @return {boolean} True if the value was set, false otherwise. */ setBackgroundUrl( container, value ) { container.style.setProperty( `--wpforms-background-url`, value ); return true; }, /** * Set container background color. * * @since 1.8.8 * * @param {HTMLElement} container Container element. * @param {string} value Value. * * @return {boolean} True if the value was set, false otherwise. */ setBackgroundColor( container, value ) { container.style.setProperty( `--wpforms-background-color`, value ); return true; }, _showBackgroundPreview( props ) { return props.attributes.backgroundImage !== 'none' && props.attributes.backgroundUrl && props.attributes.backgroundUrl !== 'url()'; }, /** * Remove background image. * * @since 1.8.8 * * @param {Function} setShowBackgroundPreview Set show background preview. * @param {Object} handlers Block handlers. * @param {Function} setLastBgImage Set last background image. */ onRemoveBackground( setShowBackgroundPreview, handlers, setLastBgImage ) { setShowBackgroundPreview( false ); handlers.styleAttrChange( 'backgroundUrl', 'url()' ); setLastBgImage( '' ); }, }; return app; }() ); integrations/gutenberg/modules/button-styles.js000064400000012225151716470030016104 0ustar00/* global wpforms_gutenberg_form_selector */ /* jshint es3: false, esversion: 6 */ /** * @param strings.border_radius * @param strings.border_size * @param strings.button_color_notice * @param strings.button_styles * @param strings.dashed * @param strings.solid */ /** * Gutenberg editor block. * * Button styles panel module. * * @since 1.8.8 */ export default ( ( function() { /** * WP core components. * * @since 1.8.8 */ const { PanelColorSettings } = wp.blockEditor || wp.editor; const { SelectControl, PanelBody, Flex, FlexBlock, __experimentalUnitControl } = wp.components; /** * Localized data aliases. * * @since 1.8.8 */ const { strings, defaults } = wpforms_gutenberg_form_selector; // noinspection UnnecessaryLocalVariableJS /** * Public functions and properties. * * @since 1.8.8 * * @type {Object} */ const app = { /** * Get block attributes. * * @since 1.8.8 * * @return {Object} Block attributes. */ getBlockAttributes() { return { buttonSize: { type: 'string', default: defaults.buttonSize, }, buttonBorderStyle: { type: 'string', default: defaults.buttonBorderStyle, }, buttonBorderSize: { type: 'string', default: defaults.buttonBorderSize, }, buttonBorderRadius: { type: 'string', default: defaults.buttonBorderRadius, }, buttonBackgroundColor: { type: 'string', default: defaults.buttonBackgroundColor, }, buttonTextColor: { type: 'string', default: defaults.buttonTextColor, }, buttonBorderColor: { type: 'string', default: defaults.buttonBorderColor, }, }; }, /** * Get Button styles JSX code. * * @since 1.8.8 * * @param {Object} props Block properties. * @param {Object} handlers Block event handlers. * @param {Object} sizeOptions Size selector options. * @param {Object} formSelectorCommon Form selector common object. * * @return {Object} Button styles JSX code. */ getButtonStyles( props, handlers, sizeOptions, formSelectorCommon ) { // eslint-disable-line max-lines-per-function return ( handlers.styleAttrChange( 'buttonSize', value ) } /> handlers.styleAttrChange( 'buttonBorderStyle', value ) } /> <__experimentalUnitControl label={ strings.border_size } value={ props.attributes.buttonBorderStyle === 'none' ? '' : props.attributes.buttonBorderSize } min={ 0 } disabled={ props.attributes.buttonBorderStyle === 'none' } onChange={ ( value ) => handlers.styleAttrChange( 'buttonBorderSize', value ) } isUnitSelectTabbable /> <__experimentalUnitControl onChange={ ( value ) => handlers.styleAttrChange( 'buttonBorderRadius', value ) } label={ strings.border_radius } min={ 0 } isUnitSelectTabbable value={ props.attributes.buttonBorderRadius } />
    { strings.colors }
    handlers.styleAttrChange( 'buttonBackgroundColor', value ), label: strings.background, }, { value: props.attributes.buttonBorderColor, onChange: ( value ) => handlers.styleAttrChange( 'buttonBorderColor', value ), label: strings.border, }, { value: props.attributes.buttonTextColor, onChange: ( value ) => handlers.styleAttrChange( 'buttonTextColor', value ), label: strings.text, }, ] } />
    { strings.button_color_notice }
    ); }, }; return app; } )() ); integrations/gutenberg/modules/common.js000064400000157266151716470030014557 0ustar00/* global jconfirm, wpforms_gutenberg_form_selector, Choices, JSX, DOM, WPFormsUtils */ /* jshint es3: false, esversion: 6 */ /** * @param strings.copy_paste_error * @param strings.error_message * @param strings.form_edit * @param strings.form_entries * @param strings.form_keywords * @param strings.form_select * @param strings.form_selected * @param strings.form_settings * @param strings.label_styles * @param strings.other_styles * @param strings.page_break * @param strings.panel_notice_head * @param strings.panel_notice_link * @param strings.panel_notice_link_text * @param strings.panel_notice_text * @param strings.show_description * @param strings.show_title * @param strings.sublabel_hints * @param strings.form_not_available_message * @param urls.entries_url * @param urls.form_url * @param window.wpforms_choicesjs_config * @param wpforms_education.upgrade_bonus * @param wpforms_gutenberg_form_selector.block_empty_url * @param wpforms_gutenberg_form_selector.block_preview_url * @param wpforms_gutenberg_form_selector.get_started_url * @param wpforms_gutenberg_form_selector.is_full_styling * @param wpforms_gutenberg_form_selector.is_modern_markup * @param wpforms_gutenberg_form_selector.logo_url * @param wpforms_gutenberg_form_selector.wpforms_guide */ /** * Gutenberg editor block. * * Common module. * * @since 1.8.8 */ export default ( function( document, window, $ ) { /** * WP core components. * * @since 1.8.8 */ const { serverSideRender: ServerSideRender = wp.components.ServerSideRender } = wp; const { createElement, Fragment, createInterpolateElement } = wp.element; const { registerBlockType } = wp.blocks; const { InspectorControls, PanelColorSettings, useBlockProps } = wp.blockEditor || wp.editor; const { SelectControl, ToggleControl, PanelBody, Placeholder } = wp.components; const { __ } = wp.i18n; const { useState, useEffect } = wp.element; /** * Localized data aliases. * * @since 1.8.8 */ const { strings, defaults, sizes, urls, isPro, isLicenseActive, isAdmin } = wpforms_gutenberg_form_selector; const defaultStyleSettings = defaults; // noinspection JSUnusedLocalSymbols /** * WPForms Education script. * * @since 1.8.8 */ const WPFormsEducation = window.WPFormsEducation || {}; // eslint-disable-line no-unused-vars /** * List of forms. * * The default value is localized in FormSelector.php. * * @since 1.8.4 * * @type {Object} */ let formList = wpforms_gutenberg_form_selector.forms; /** * Blocks runtime data. * * @since 1.8.1 * * @type {Object} */ const blocks = {}; /** * Whether it is needed to trigger server rendering. * * @since 1.8.1 * * @type {boolean} */ let triggerServerRender = true; /** * Popup container. * * @since 1.8.3 * * @type {Object} */ let $popup = {}; /** * Track fetch status. * * @since 1.8.4 * * @type {boolean} */ let isFetching = false; /** * Elements holder. * * @since 1.8.8 * * @type {Object} */ const el = {}; /** * Common block attributes. * * @since 1.8.8 * * @type {Object} */ let commonAttributes = { clientId: { type: 'string', default: '', }, formId: { type: 'string', default: defaultStyleSettings.formId, }, displayTitle: { type: 'boolean', default: defaultStyleSettings.displayTitle, }, displayDesc: { type: 'boolean', default: defaultStyleSettings.displayDesc, }, preview: { type: 'boolean', }, theme: { type: 'string', default: defaultStyleSettings.theme, }, themeName: { type: 'string', default: defaultStyleSettings.themeName, }, labelSize: { type: 'string', default: defaultStyleSettings.labelSize, }, labelColor: { type: 'string', default: defaultStyleSettings.labelColor, }, labelSublabelColor: { type: 'string', default: defaultStyleSettings.labelSublabelColor, }, labelErrorColor: { type: 'string', default: defaultStyleSettings.labelErrorColor, }, pageBreakColor: { type: 'string', default: defaultStyleSettings.pageBreakColor, }, customCss: { type: 'string', default: defaultStyleSettings.customCss, }, copyPasteJsonValue: { type: 'string', default: defaultStyleSettings.copyPasteJsonValue, }, }; /** * Handlers for custom styles settings, defined outside this module. * * @since 1.8.8 * * @type {Object} */ let customStylesHandlers = {}; /** * Dropdown timeout. * * @since 1.8.8 * * @type {number} */ let dropdownTimeout; /** * Whether copy-paste content was generated on edit. * * @since 1.9.1 * * @type {boolean} */ let isCopyPasteGeneratedOnEdit = false; /** * Whether the background is selected. * * @since 1.9.3 * * @type {boolean} */ let backgroundSelected = false; /** * Public functions and properties. * * @since 1.8.1 * * @type {Object} */ const app = { /** * Panel modules. * * @since 1.8.8 * * @type {Object} */ panels: {}, /** * Start the engine. * * @since 1.8.1 * * @param {Object} blockOptions Block options. */ init( blockOptions ) { el.$window = $( window ); app.panels = blockOptions.panels; app.education = blockOptions.education; app.initDefaults( blockOptions ); app.registerBlock( blockOptions ); app.initJConfirm(); $( app.ready ); }, /** * Document ready. * * @since 1.8.1 */ ready() { app.events(); }, /** * Events. * * @since 1.8.1 */ events() { el.$window .on( 'wpformsFormSelectorEdit', _.debounce( app.blockEdit, 250 ) ) .on( 'wpformsFormSelectorFormLoaded', app.formLoaded ); }, /** * Init jConfirm. * * @since 1.8.8 */ initJConfirm() { // jquery-confirm defaults. jconfirm.defaults = { closeIcon: false, backgroundDismiss: false, escapeKey: true, animationBounce: 1, useBootstrap: false, theme: 'modern', boxWidth: '400px', animateFromElement: false, }; }, /** * Get a fresh list of forms via REST-API. * * @since 1.8.4 * * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-api-fetch/ */ async getForms() { // If a fetch is already in progress, exit the function. if ( isFetching ) { return; } // Set the flag to true indicating a fetch is in progress. isFetching = true; try { // Fetch forms. formList = await wp.apiFetch( { path: wpforms_gutenberg_form_selector.route_namespace + 'forms/', method: 'GET', cache: 'no-cache', } ); } catch ( error ) { // eslint-disable-next-line no-console console.error( error ); } finally { isFetching = false; } }, /** * Open builder popup. * * @since 1.6.2 * * @param {string} clientID Block Client ID. */ openBuilderPopup( clientID ) { if ( $.isEmptyObject( $popup ) ) { const parent = $( '#wpwrap' ); const canvasIframe = $( 'iframe[name="editor-canvas"]' ); const isFseMode = Boolean( canvasIframe.length ); const tmpl = isFseMode ? canvasIframe.contents().find( '#wpforms-gutenberg-popup' ) : $( '#wpforms-gutenberg-popup' ); parent.after( tmpl ); $popup = parent.siblings( '#wpforms-gutenberg-popup' ); } const url = wpforms_gutenberg_form_selector.get_started_url, $iframe = $popup.find( 'iframe' ); app.builderCloseButtonEvent( clientID ); $iframe.attr( 'src', url ); $popup.fadeIn(); }, /** * Close button (inside the form builder) click event. * * @since 1.8.3 * * @param {string} clientID Block Client ID. */ builderCloseButtonEvent( clientID ) { $popup .off( 'wpformsBuilderInPopupClose' ) .on( 'wpformsBuilderInPopupClose', function( e, action, formId, formTitle ) { if ( action !== 'saved' || ! formId ) { return; } // Insert a new block when a new form is created from the popup to update the form list and attributes. const newBlock = wp.blocks.createBlock( 'wpforms/form-selector', { formId: formId.toString(), // Expects string value, make sure we insert string. } ); // eslint-disable-next-line camelcase formList = [ { ID: formId, post_title: formTitle } ]; // Insert a new block. wp.data.dispatch( 'core/block-editor' ).removeBlock( clientID ); wp.data.dispatch( 'core/block-editor' ).insertBlocks( newBlock ); } ); }, /** * Register block. * * @since 1.8.1 * * @param {Object} blockOptions Additional block options. */ // eslint-disable-next-line max-lines-per-function registerBlock( blockOptions ) { registerBlockType( 'wpforms/form-selector', { title: strings.title, description: strings.description, icon: app.getIcon(), keywords: strings.form_keywords, category: 'widgets', attributes: app.getBlockAttributes(), supports: { customClassName: app.hasForms(), }, example: { attributes: { preview: true, }, }, // eslint-disable-next-line max-lines-per-function,complexity edit( props ) { const { attributes } = props; const formOptions = app.getFormOptions(); const handlers = app.getSettingsFieldsHandlers( props ); const [ isNotDisabled ] = useState( isPro && isLicenseActive ); // eslint-disable-line react-hooks/rules-of-hooks const [ isProEnabled ] = useState( isPro ); // eslint-disable-line react-hooks/rules-of-hooks, no-unused-vars const [ showBackgroundPreview, setShowBackgroundPreview ] = useState( blockOptions.panels.background._showBackgroundPreview( props ) ); // eslint-disable-line react-hooks/rules-of-hooks const [ lastBgImage, setLastBgImage ] = useState( '' ); // eslint-disable-line react-hooks/rules-of-hooks const uiState = { isNotDisabled, isProEnabled, showBackgroundPreview, setShowBackgroundPreview, lastBgImage, setLastBgImage, }; useEffect( () => { // eslint-disable-line react-hooks/rules-of-hooks if ( attributes.formId ) { setShowBackgroundPreview( props.attributes.backgroundImage !== 'none' && props.attributes.backgroundUrl && props.attributes.backgroundUrl !== 'url()' ); } }, [ backgroundSelected, props.attributes.backgroundImage, props.attributes.backgroundUrl ] ); // eslint-disable-line react-hooks/exhaustive-deps // Get block properties. const blockProps = useBlockProps(); // eslint-disable-line react-hooks/rules-of-hooks, no-unused-vars // Store block clientId in attributes. if ( ! attributes.clientId || ! app.isClientIdAttrUnique( props ) ) { // We just want the client ID to update once. // The block editor doesn't have a fixed block ID, so we need to get it on the initial load, but only once. props.setAttributes( { clientId: props.clientId } ); } // Main block settings. const jsx = [ app.jsxParts.getMainSettings( attributes, handlers, formOptions ), ]; // Block preview picture. if ( ! app.hasForms() ) { jsx.push( app.jsxParts.getEmptyFormsPreview( props ), ); return
    { jsx }
    ; } const sizeOptions = app.getSizeOptions(); // Show placeholder when form is not available (trashed, deleted etc.). if ( attributes && attributes.formId && app.isFormAvailable( attributes.formId ) === false ) { // Block placeholder (form selector). jsx.push( app.jsxParts.getBlockPlaceholder( props.attributes, handlers, formOptions ), ); return
    { jsx }
    ; } // Form style settings & block content. if ( attributes.formId ) { // Subscribe to block events. app.maybeSubscribeToBlockEvents( props, handlers, blockOptions ); jsx.push( app.jsxParts.getStyleSettings( props, handlers, sizeOptions, blockOptions, uiState ), app.jsxParts.getBlockFormContent( props ) ); if ( ! isCopyPasteGeneratedOnEdit ) { handlers.updateCopyPasteContent(); isCopyPasteGeneratedOnEdit = true; } el.$window.trigger( 'wpformsFormSelectorEdit', [ props ] ); return
    { jsx }
    ; } // Block preview picture. if ( attributes.preview ) { jsx.push( app.jsxParts.getBlockPreview(), ); return
    { jsx }
    ; } // Block placeholder (form selector). jsx.push( app.jsxParts.getBlockPlaceholder( props.attributes, handlers, formOptions ), ); return
    { jsx }
    ; }, save: () => null, } ); }, /** * Init default style settings. * * @since 1.8.1 * @since 1.8.8 Added blockOptions parameter. * * @param {Object} blockOptions Additional block options. */ initDefaults( blockOptions = {} ) { commonAttributes = { ...commonAttributes, ...blockOptions.getCommonAttributes(), }; customStylesHandlers = blockOptions.setStylesHandlers; [ 'formId', 'copyPasteJsonValue' ].forEach( ( key ) => delete defaultStyleSettings[ key ] ); }, /** * Check if the site has forms. * * @since 1.8.3 * * @return {boolean} Whether site has at least one form. */ hasForms() { return formList.length > 0; }, /** * Check if form is available to be previewed. * * @since 1.8.9 * * @param {number} formId Form ID. * * @return {boolean} Whether form is available. */ isFormAvailable( formId ) { return formList.find( ( { ID } ) => ID === Number( formId ) ) !== undefined; }, /** * Set triggerServerRender flag. * * @since 1.8.8 * * @param {boolean} $flag The value of the triggerServerRender flag. */ setTriggerServerRender( $flag ) { triggerServerRender = Boolean( $flag ); }, /** * Maybe subscribe to block events. * * @since 1.8.8 * * @param {Object} subscriberProps Subscriber block properties. * @param {Object} subscriberHandlers Subscriber block event handlers. * @param {Object} subscriberBlockOptions Subscriber block options. */ maybeSubscribeToBlockEvents( subscriberProps, subscriberHandlers, subscriberBlockOptions ) { const id = subscriberProps.clientId; // Unsubscribe from block events. // This is needed to avoid multiple subscriptions when the block is re-rendered. el.$window .off( 'wpformsFormSelectorDeleteTheme.' + id ) .off( 'wpformsFormSelectorUpdateTheme.' + id ) .off( 'wpformsFormSelectorSetTheme.' + id ); // Subscribe to block events. el.$window .on( 'wpformsFormSelectorDeleteTheme.' + id, app.subscriberDeleteTheme( subscriberProps, subscriberBlockOptions ) ) .on( 'wpformsFormSelectorUpdateTheme.' + id, app.subscriberUpdateTheme( subscriberProps, subscriberBlockOptions ) ) .on( 'wpformsFormSelectorSetTheme.' + id, app.subscriberSetTheme( subscriberProps, subscriberBlockOptions ) ); }, /** * Block event `wpformsFormSelectorDeleteTheme` handler. * * @since 1.8.8 * * @param {Object} subscriberProps Subscriber block properties * @param {Object} subscriberBlockOptions Subscriber block options. * * @return {Function} Event handler. */ subscriberDeleteTheme( subscriberProps, subscriberBlockOptions ) { return function( e, themeSlug, triggerProps ) { if ( subscriberProps.clientId === triggerProps.clientId ) { return; } if ( subscriberProps?.attributes?.theme !== themeSlug ) { return; } if ( ! subscriberBlockOptions?.panels?.themes ) { return; } // Reset theme to default one. subscriberBlockOptions.panels.themes.setBlockTheme( subscriberProps, 'default' ); }; }, /** * Block event `wpformsFormSelectorDeleteTheme` handler. * * @since 1.8.8 * * @param {Object} subscriberProps Subscriber block properties * @param {Object} subscriberBlockOptions Subscriber block options. * * @return {Function} Event handler. */ subscriberUpdateTheme( subscriberProps, subscriberBlockOptions ) { return function( e, themeSlug, themeData, triggerProps ) { if ( subscriberProps.clientId === triggerProps.clientId ) { return; } if ( subscriberProps?.attributes?.theme !== themeSlug ) { return; } if ( ! subscriberBlockOptions?.panels?.themes ) { return; } // Reset theme to default one. subscriberBlockOptions.panels.themes.setBlockTheme( subscriberProps, themeSlug ); }; }, /** * Block event `wpformsFormSelectorSetTheme` handler. * * @since 1.8.8 * * @param {Object} subscriberProps Subscriber block properties * @param {Object} subscriberBlockOptions Subscriber block options. * * @return {Function} Event handler. */ subscriberSetTheme( subscriberProps, subscriberBlockOptions ) { // noinspection JSUnusedLocalSymbols return function( e, block, themeSlug, triggerProps ) { // eslint-disable-line no-unused-vars if ( subscriberProps.clientId === triggerProps.clientId ) { return; } if ( ! subscriberBlockOptions?.panels?.themes ) { return; } // Set theme. app.onSetTheme( subscriberProps ); }; }, /** * Block JSX parts. * * @since 1.8.1 * * @type {Object} */ jsxParts: { /** * Get main settings JSX code. * * @since 1.8.1 * * @param {Object} attributes Block attributes. * @param {Object} handlers Block event handlers. * @param {Object} formOptions Form selector options. * * @return {JSX.Element} Main setting JSX code. */ getMainSettings( attributes, handlers, formOptions ) { if ( ! app.hasForms() ) { return app.jsxParts.printEmptyFormsNotice( attributes.clientId ); } return ( handlers.attrChange( 'formId', value ) } /> { attributes.formId ? ( <>

    { strings.form_edit } { isPro && isLicenseActive && ( <>   |   { strings.form_entries } ) }

    handlers.attrChange( 'displayTitle', value ) } /> handlers.attrChange( 'displayDesc', value ) } /> ) : null }

    { strings.panel_notice_head } { strings.panel_notice_text } { strings.panel_notice_link_text }

    ); }, /** * Print empty forms notice. * * @since 1.8.3 * * @param {string} clientId Block client ID. * * @return {JSX.Element} Field styles JSX code. */ printEmptyFormsNotice( clientId ) { return (

    { __( 'You haven’t created a form, yet!', 'wpforms-lite' ) } { __( 'What are you waiting for?', 'wpforms-lite' ) }

    ); }, /** * Get Label styles JSX code. * * @since 1.8.1 * * @param {Object} props Block properties. * @param {Object} handlers Block event handlers. * @param {Object} sizeOptions Size selector options. * * @return {Object} Label styles JSX code. */ getLabelStyles( props, handlers, sizeOptions ) { return ( handlers.styleAttrChange( 'labelSize', value ) } />
    { strings.colors }
    handlers.styleAttrChange( 'labelColor', value ), label: strings.label, }, { value: props.attributes.labelSublabelColor, onChange: ( value ) => handlers.styleAttrChange( 'labelSublabelColor', value ), label: strings.sublabel_hints.replace( '&', '&' ), }, { value: props.attributes.labelErrorColor, onChange: ( value ) => handlers.styleAttrChange( 'labelErrorColor', value ), label: strings.error_message, }, ] } />
    ); }, /** * Get Page Indicator styles JSX code. * * @since 1.8.7 * * @param {Object} props Block properties. * @param {Object} handlers Block event handlers. * * @return {Object} Page Indicator styles JSX code. */ getPageIndicatorStyles( props, handlers ) { // eslint-disable-line complexity const hasPageBreak = app.hasPageBreak( formList, props.attributes.formId ); const hasRating = app.hasRating( formList, props.attributes.formId ); if ( ! hasPageBreak && ! hasRating ) { return null; } let label = ''; if ( hasPageBreak && hasRating ) { label = `${ strings.page_break } / ${ strings.rating }`; } else if ( hasPageBreak ) { label = strings.page_break; } else if ( hasRating ) { label = strings.rating; } return (
    { strings.colors }
    handlers.styleAttrChange( 'pageBreakColor', value ), label, }, ] } />
    ); }, /** * Get style settings JSX code. * * @since 1.8.1 * * @param {Object} props Block properties. * @param {Object} handlers Block event handlers. * @param {Object} sizeOptions Size selector options. * @param {Object} blockOptions Block options loaded from external modules. * * @param {Object} uiState UI state. * * @return {Object} Inspector controls JSX code. */ getStyleSettings( props, handlers, sizeOptions, blockOptions, uiState ) { return ( { blockOptions.getThemesPanel( props, app, blockOptions.stockPhotos ) } { blockOptions.getFieldStyles( props, handlers, sizeOptions, app ) } { app.jsxParts.getLabelStyles( props, handlers, sizeOptions ) } { blockOptions.getButtonStyles( props, handlers, sizeOptions, app ) } { blockOptions.getContainerStyles( props, handlers, app, uiState ) } { blockOptions.getBackgroundStyles( props, handlers, app, blockOptions.stockPhotos, uiState ) } { app.jsxParts.getPageIndicatorStyles( props, handlers ) } ); }, /** * Get block content JSX code. * * @since 1.8.1 * * @param {Object} props Block properties. * * @return {JSX.Element} Block content JSX code. */ getBlockFormContent( props ) { if ( triggerServerRender ) { return ( ); } const clientId = props.clientId; const block = app.getBlockContainer( props ); // In the case of empty content, use server side renderer. // This happens when the block is duplicated or converted to a reusable block. if ( ! block?.innerHTML ) { triggerServerRender = true; return app.jsxParts.getBlockFormContent( props ); } blocks[ clientId ] = blocks[ clientId ] || {}; blocks[ clientId ].blockHTML = block.innerHTML; blocks[ clientId ].loadedFormId = props.attributes.formId; return (
    ); }, /** * Get block preview JSX code. * * @since 1.8.1 * * @return {JSX.Element} Block preview JSX code. */ getBlockPreview() { return ( ); }, /** * Get block empty JSX code. * * @since 1.8.3 * * @param {Object} props Block properties. * @return {JSX.Element} Block empty JSX code. */ getEmptyFormsPreview( props ) { const clientId = props.clientId; return (

    { createInterpolateElement( __( 'You can use WPForms to build contact forms, surveys, payment forms, and more with just a few clicks.', 'wpforms-lite' ), { b: , } ) }

    { createInterpolateElement( __( 'Need some help? Check out our comprehensive guide.', 'wpforms-lite' ), { // eslint-disable-next-line jsx-a11y/anchor-has-content a: , } ) }

    { /* Template for popup with builder iframe */ }
    ); }, /** * Get block placeholder (form selector) JSX code. * * @since 1.8.1 * * @param {Object} attributes Block attributes. * @param {Object} handlers Block event handlers. * @param {Object} formOptions Form selector options. * * @return {JSX.Element} Block placeholder JSX code. */ getBlockPlaceholder( attributes, handlers, formOptions ) { const isFormNotAvailable = attributes.formId && ! app.isFormAvailable( attributes.formId ); return ( { isFormNotAvailable && (

    { strings.form_not_available_message }

    ) } handlers.attrChange( 'formId', value ) } />
    ); }, }, /** * Determine if the form has a Page Break field. * * @since 1.8.7 * * @param {Object} forms The forms' data object. * @param {number|string} formId Form ID. * * @return {boolean} True when the form has a Page Break field, false otherwise. */ hasPageBreak( forms, formId ) { const currentForm = forms.find( ( form ) => parseInt( form.ID, 10 ) === parseInt( formId, 10 ) ); if ( ! currentForm.post_content ) { return false; } const fields = JSON.parse( currentForm.post_content )?.fields; return Object.values( fields ).some( ( field ) => field.type === 'pagebreak' ); }, hasRating( forms, formId ) { const currentForm = forms.find( ( form ) => parseInt( form.ID, 10 ) === parseInt( formId, 10 ) ); if ( ! currentForm.post_content || ! isPro || ! isLicenseActive ) { return false; } const fields = JSON.parse( currentForm.post_content )?.fields; return Object.values( fields ).some( ( field ) => field.type === 'rating' ); }, /** * Get Style Settings panel class. * * @since 1.8.1 * * @param {Object} props Block properties. * @param {string} panel Panel name. * * @return {string} Style Settings panel class. */ getPanelClass( props, panel = '' ) { let cssClass = 'wpforms-gutenberg-panel wpforms-block-settings-' + props.clientId; if ( ! app.isFullStylingEnabled() ) { cssClass += ' disabled_panel'; } // Restrict styling panel for non-admins. if ( ! ( isAdmin || panel === 'themes' ) ) { cssClass += ' wpforms-gutenberg-panel-restricted'; } return cssClass; }, /** * Get color panel settings CSS class. * * @since 1.8.8 * * @param {string} borderStyle Border style value. * * @return {string} Style Settings panel class. */ getColorPanelClass( borderStyle ) { let cssClass = 'wpforms-gutenberg-form-selector-color-panel'; if ( borderStyle === 'none' ) { cssClass += ' wpforms-gutenberg-form-selector-border-color-disabled'; } return cssClass; }, /** * Determine whether the full styling is enabled. * * @since 1.8.1 * * @return {boolean} Whether the full styling is enabled. */ isFullStylingEnabled() { return wpforms_gutenberg_form_selector.is_modern_markup && wpforms_gutenberg_form_selector.is_full_styling; }, /** * Determine whether the block has lead forms enabled. * * @since 1.9.0 * * @param {Object} block Gutenberg block * * @return {boolean} Whether the block has lead forms enabled */ isLeadFormsEnabled( block ) { if ( ! block ) { return false; } const $form = $( block.querySelector( '.wpforms-container' ) ); return $form.hasClass( 'wpforms-lead-forms-container' ); }, /** * Get block container DOM element. * * @since 1.8.1 * * @param {Object} props Block properties. * * @return {Element} Block container. */ getBlockContainer( props ) { const blockSelector = `#block-${ props.clientId } > div`; let block = document.querySelector( blockSelector ); // For FSE / Gutenberg plugin, we need to take a look inside the iframe. if ( ! block ) { const editorCanvas = document.querySelector( 'iframe[name="editor-canvas"]' ); block = editorCanvas?.contentWindow.document.querySelector( blockSelector ); } return block; }, /** * Get form container in Block Editor. * * @since 1.9.3 * * @param {number} formId Form ID. * * @return {Element|null} Form container. */ getFormBlock( formId ) { // First, try to find the iframe for blocks version 3. const editorCanvas = document.querySelector( 'iframe[name="editor-canvas"]' ); // If the iframe is found, try to find the form. return editorCanvas?.contentWindow.document.querySelector( `#wpforms-${ formId }` ) || $( `#wpforms-${ formId }` ); }, /** * Update CSS variable(s) value(s) of the given attribute for given container on the preview. * * @since 1.8.8 * * @param {string} attribute Style attribute: field-size, label-size, button-size, etc. * @param {string} value Property new value. * @param {Element} container Form container. * @param {Object} props Block properties. */ updatePreviewCSSVarValue( attribute, value, container, props ) { // eslint-disable-line complexity, max-lines-per-function if ( ! container || ! attribute ) { return; } const property = attribute.replace( /[A-Z]/g, ( letter ) => `-${ letter.toLowerCase() }` ); if ( typeof customStylesHandlers[ property ] === 'function' ) { customStylesHandlers[ property ]( container, value ); return; } switch ( property ) { case 'field-size': case 'label-size': case 'button-size': case 'container-shadow-size': for ( const key in sizes[ property ][ value ] ) { container.style.setProperty( `--wpforms-${ property }-${ key }`, sizes[ property ][ value ][ key ], ); } break; case 'field-border-style': if ( value === 'none' ) { app.toggleFieldBorderNoneCSSVarValue( container, true ); } else { app.toggleFieldBorderNoneCSSVarValue( container, false ); container.style.setProperty( `--wpforms-${ property }`, value ); } break; case 'button-background-color': app.maybeUpdateAccentColor( props.attributes.buttonBorderColor, value, container ); value = app.maybeSetButtonAltBackgroundColor( value, props.attributes.buttonBorderColor, container ); app.maybeSetButtonAltTextColor( props.attributes.buttonTextColor, value, props.attributes.buttonBorderColor, container ); container.style.setProperty( `--wpforms-${ property }`, value ); break; case 'button-border-color': app.maybeUpdateAccentColor( value, props.attributes.buttonBackgroundColor, container ); app.maybeSetButtonAltTextColor( props.attributes.buttonTextColor, props.attributes.buttonBackgroundColor, value, container ); container.style.setProperty( `--wpforms-${ property }`, value ); break; case 'button-text-color': app.maybeSetButtonAltTextColor( value, props.attributes.buttonBackgroundColor, props.attributes.buttonBorderColor, container ); container.style.setProperty( `--wpforms-${ property }`, value ); break; default: container.style.setProperty( `--wpforms-${ property }`, value ); container.style.setProperty( `--wpforms-${ property }-spare`, value ); } }, /** * Set/unset field border vars in case of border-style is none. * * @since 1.8.8 * * @param {Object} container Form container. * @param {boolean} set True when set, false when unset. */ toggleFieldBorderNoneCSSVarValue( container, set ) { const cont = container.querySelector( 'form' ); if ( set ) { cont.style.setProperty( '--wpforms-field-border-style', 'solid' ); cont.style.setProperty( '--wpforms-field-border-size', '1px' ); cont.style.setProperty( '--wpforms-field-border-color', 'transparent' ); return; } cont.style.setProperty( '--wpforms-field-border-style', null ); cont.style.setProperty( '--wpforms-field-border-size', null ); cont.style.setProperty( '--wpforms-field-border-color', null ); }, /** * Maybe set the button's alternative background color. * * @since 1.8.8 * * @param {string} value Attribute value. * @param {string} buttonBorderColor Button border color. * @param {Object} container Form container. * * @return {string|*} New background color. */ maybeSetButtonAltBackgroundColor( value, buttonBorderColor, container ) { // Setting css property value to child `form` element overrides the parent property value. const form = container.querySelector( 'form' ); form.style.setProperty( '--wpforms-button-background-color-alt', value ); if ( WPFormsUtils.cssColorsUtils.isTransparentColor( value ) ) { return WPFormsUtils.cssColorsUtils.isTransparentColor( buttonBorderColor ) ? defaultStyleSettings.buttonBackgroundColor : buttonBorderColor; } return value; }, /** * Maybe set the button's alternative text color. * * @since 1.8.8 * * @param {string} value Attribute value. * @param {string} buttonBackgroundColor Button background color. * @param {string} buttonBorderColor Button border color. * @param {Object} container Form container. */ maybeSetButtonAltTextColor( value, buttonBackgroundColor, buttonBorderColor, container ) { const form = container.querySelector( 'form' ); let altColor = null; value = value.toLowerCase(); if ( WPFormsUtils.cssColorsUtils.isTransparentColor( value ) || value === buttonBackgroundColor || ( WPFormsUtils.cssColorsUtils.isTransparentColor( buttonBackgroundColor ) && value === buttonBorderColor ) ) { altColor = WPFormsUtils.cssColorsUtils.getContrastColor( buttonBackgroundColor ); } container.style.setProperty( `--wpforms-button-text-color-alt`, value ); form.style.setProperty( `--wpforms-button-text-color-alt`, altColor ); }, /** * Maybe update accent color. * * @since 1.8.8 * * @param {string} color Color value. * @param {string} buttonBackgroundColor Button background color. * @param {Object} container Form container. */ maybeUpdateAccentColor( color, buttonBackgroundColor, container ) { // Setting css property value to child `form` element overrides the parent property value. const form = container.querySelector( 'form' ); // Fallback to default color if the border color is transparent. color = WPFormsUtils.cssColorsUtils.isTransparentColor( color ) ? defaultStyleSettings.buttonBackgroundColor : color; if ( WPFormsUtils.cssColorsUtils.isTransparentColor( buttonBackgroundColor ) ) { form.style.setProperty( '--wpforms-button-background-color-alt', 'rgba( 0, 0, 0, 0 )' ); form.style.setProperty( '--wpforms-button-background-color', color ); } else { container.style.setProperty( '--wpforms-button-background-color-alt', buttonBackgroundColor ); form.style.setProperty( '--wpforms-button-background-color-alt', null ); form.style.setProperty( '--wpforms-button-background-color', null ); } }, /** * Get settings fields event handlers. * * @since 1.8.1 * * @param {Object} props Block properties. * * @return {Object} Object that contains event handlers for the settings fields. */ getSettingsFieldsHandlers( props ) { // eslint-disable-line max-lines-per-function return { /** * Field style attribute change event handler. * * @since 1.8.1 * * @param {string} attribute Attribute name. * @param {string} value New attribute value. */ styleAttrChange( attribute, value ) { const block = app.getBlockContainer( props ), container = block.querySelector( `#wpforms-${ props.attributes.formId }` ), setAttr = {}; // Unset the color means setting the transparent color. if ( attribute.includes( 'Color' ) ) { value = value ?? 'rgba( 0, 0, 0, 0 )'; } app.updatePreviewCSSVarValue( attribute, value, container, props ); setAttr[ attribute ] = value; app.setBlockRuntimeStateVar( props.clientId, 'prevAttributesState', props.attributes ); props.setAttributes( setAttr ); triggerServerRender = false; this.updateCopyPasteContent(); app.panels.themes.updateCustomThemeAttribute( attribute, value, props ); this.maybeToggleDropdown( props, attribute ); // Trigger event for developers. el.$window.trigger( 'wpformsFormSelectorStyleAttrChange', [ block, props, attribute, value ] ); }, /** * Handles the toggling of the dropdown menu's visibility. * * @since 1.8.8 * * @param {Object} props The block properties. * @param {string} attribute The name of the attribute being changed. */ maybeToggleDropdown( props, attribute ) { // eslint-disable-line no-shadow const formId = props.attributes.formId; const menu = document.querySelector( `#wpforms-form-${ formId } .choices__list.choices__list--dropdown` ); const classicMenu = document.querySelector( `#wpforms-form-${ formId } .wpforms-field-select-style-classic select` ); if ( attribute === 'fieldMenuColor' ) { if ( menu ) { menu.classList.add( 'is-active' ); menu.parentElement.classList.add( 'is-open' ); } else { this.showClassicMenu( classicMenu ); } clearTimeout( dropdownTimeout ); dropdownTimeout = setTimeout( () => { const toClose = document.querySelector( `#wpforms-form-${ formId } .choices__list.choices__list--dropdown` ); if ( toClose ) { toClose.classList.remove( 'is-active' ); toClose.parentElement.classList.remove( 'is-open' ); } else { this.hideClassicMenu( document.querySelector( `#wpforms-form-${ formId } .wpforms-field-select-style-classic select` ) ); } }, 5000 ); } else if ( menu ) { menu.classList.remove( 'is-active' ); } else { this.hideClassicMenu( classicMenu ); } }, /** * Shows the classic menu. * * @since 1.8.8 * * @param {Object} classicMenu The classic menu. */ showClassicMenu( classicMenu ) { if ( ! classicMenu ) { return; } classicMenu.size = 2; classicMenu.style.cssText = 'padding-top: 40px; padding-inline-end: 0; padding-inline-start: 0; position: relative;'; classicMenu.querySelectorAll( 'option' ).forEach( ( option ) => { option.style.cssText = 'border-left: 1px solid #8c8f94; border-right: 1px solid #8c8f94; padding: 0 10px; z-index: 999999; position: relative;'; } ); classicMenu.querySelector( 'option:last-child' ).style.cssText = 'border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; padding: 0 10px; border-left: 1px solid #8c8f94; border-right: 1px solid #8c8f94; border-bottom: 1px solid #8c8f94; z-index: 999999; position: relative;'; }, /** * Hides the classic menu. * * @since 1.8.8 * * @param {Object} classicMenu The classic menu. */ hideClassicMenu( classicMenu ) { if ( ! classicMenu ) { return; } classicMenu.size = 0; classicMenu.style.cssText = 'padding-top: 0; padding-inline-end: 24px; padding-inline-start: 12px; position: relative;'; classicMenu.querySelectorAll( 'option' ).forEach( ( option ) => { option.style.cssText = 'border: none;'; } ); }, /** * Field regular attribute change event handler. * * @since 1.8.1 * * @param {string} attribute Attribute name. * @param {string} value New attribute value. */ attrChange( attribute, value ) { const setAttr = {}; setAttr[ attribute ] = value; app.setBlockRuntimeStateVar( props.clientId, 'prevAttributesState', props.attributes ); props.setAttributes( setAttr ); triggerServerRender = true; this.updateCopyPasteContent(); }, /** * Update content of the "Copy/Paste" fields. * * @since 1.8.1 */ updateCopyPasteContent() { const content = {}; const atts = wp.data.select( 'core/block-editor' ).getBlockAttributes( props.clientId ); for ( const key in defaultStyleSettings ) { content[ key ] = atts[ key ]; } props.setAttributes( { copyPasteJsonValue: JSON.stringify( content ) } ); }, /** * Paste settings handler. * * @since 1.8.1 * * @param {string} value New attribute value. */ pasteSettings( value ) { value = value.trim(); const pasteAttributes = app.parseValidateJson( value ); if ( ! pasteAttributes ) { if ( value ) { wp.data.dispatch( 'core/notices' ).createErrorNotice( strings.copy_paste_error, { id: 'wpforms-json-parse-error' } ); } this.updateCopyPasteContent(); return; } pasteAttributes.copyPasteJsonValue = value; const themeSlug = app.panels.themes.maybeCreateCustomThemeFromAttributes( pasteAttributes ); app.setBlockRuntimeStateVar( props.clientId, 'prevAttributesState', props.attributes ); props.setAttributes( pasteAttributes ); app.panels.themes.setBlockTheme( props, themeSlug ); triggerServerRender = false; }, }; }, /** * Parse and validate JSON string. * * @since 1.8.1 * * @param {string} value JSON string. * * @return {boolean|object} Parsed JSON object OR false on error. */ parseValidateJson( value ) { if ( typeof value !== 'string' ) { return false; } let atts; try { atts = JSON.parse( value.trim() ); } catch ( error ) { atts = false; } return atts; }, /** * Get WPForms icon DOM element. * * @since 1.8.1 * * @return {DOM.element} WPForms icon DOM element. */ getIcon() { return createElement( 'svg', { width: 20, height: 20, viewBox: '0 0 612 612', className: 'dashicon' }, createElement( 'path', { fill: 'currentColor', d: 'M544,0H68C30.445,0,0,30.445,0,68v476c0,37.556,30.445,68,68,68h476c37.556,0,68-30.444,68-68V68 C612,30.445,581.556,0,544,0z M464.44,68L387.6,120.02L323.34,68H464.44z M288.66,68l-64.26,52.02L147.56,68H288.66z M544,544H68 V68h22.1l136,92.14l79.9-64.6l79.56,64.6l136-92.14H544V544z M114.24,263.16h95.88v-48.28h-95.88V263.16z M114.24,360.4h95.88 v-48.62h-95.88V360.4z M242.76,360.4h255v-48.62h-255V360.4L242.76,360.4z M242.76,263.16h255v-48.28h-255V263.16L242.76,263.16z M368.22,457.3h129.54V408H368.22V457.3z', }, ), ); }, /** * Get WPForms blocks. * * @since 1.8.8 * * @return {Array} Blocks array. */ getWPFormsBlocks() { const wpformsBlocks = wp.data.select( 'core/block-editor' ).getBlocks(); return wpformsBlocks.filter( ( props ) => { return props.name === 'wpforms/form-selector'; } ); }, /** * Get WPForms blocks. * * @since 1.8.8 * * @param {Object} props Block properties. * * @return {Object} Block attributes. */ isClientIdAttrUnique( props ) { const wpformsBlocks = app.getWPFormsBlocks(); for ( const key in wpformsBlocks ) { // Skip the current block. if ( wpformsBlocks[ key ].clientId === props.clientId ) { continue; } if ( wpformsBlocks[ key ].attributes.clientId === props.attributes.clientId ) { return false; } } return true; }, /** * Get block attributes. * * @since 1.8.1 * * @return {Object} Block attributes. */ getBlockAttributes() { return commonAttributes; }, /** * Get block runtime state variable. * * @since 1.8.8 * * @param {string} clientId Block client ID. * @param {string} varName Block runtime variable name. * * @return {*} Block runtime state variable value. */ getBlockRuntimeStateVar( clientId, varName ) { return blocks[ clientId ]?.[ varName ]; }, /** * Set block runtime state variable value. * * @since 1.8.8 * * @param {string} clientId Block client ID. * @param {string} varName Block runtime state key. * @param {*} value State variable value. * * @return {boolean} True on success. */ setBlockRuntimeStateVar( clientId, varName, value ) { // eslint-disable-line complexity if ( ! clientId || ! varName ) { return false; } blocks[ clientId ] = blocks[ clientId ] || {}; blocks[ clientId ][ varName ] = value; // Prevent referencing to object. if ( typeof value === 'object' && ! Array.isArray( value ) && value !== null ) { blocks[ clientId ][ varName ] = { ...value }; } return true; }, /** * Get form selector options. * * @since 1.8.1 * * @return {Array} Form options. */ getFormOptions() { const formOptions = formList.map( ( value ) => ( { value: value.ID, label: value.post_title } ) ); formOptions.unshift( { value: '', label: strings.form_select } ); return formOptions; }, /** * Get size selector options. * * @since 1.8.1 * * @return {Array} Size options. */ getSizeOptions() { return [ { label: strings.small, value: 'small', }, { label: strings.medium, value: 'medium', }, { label: strings.large, value: 'large', }, ]; }, /** * Event `wpformsFormSelectorEdit` handler. * * @since 1.8.1 * * @param {Object} e Event object. * @param {Object} props Block properties. */ blockEdit( e, props ) { const block = app.getBlockContainer( props ); if ( ! block?.dataset ) { return; } app.initLeadFormSettings( block.parentElement ); }, /** * Init Lead Form Settings panels. * * @since 1.8.1 * * @param {Element} block Block element. * @param {Object} block.dataset Block element. */ initLeadFormSettings( block ) { if ( ! block?.dataset ) { return; } if ( ! app.isFullStylingEnabled() ) { return; } const clientId = block.dataset.block; const $panel = $( `.wpforms-block-settings-${ clientId }` ); if ( app.isLeadFormsEnabled( block ) ) { $panel .addClass( 'disabled_panel' ) .find( '.wpforms-gutenberg-panel-notice.wpforms-lead-form-notice' ) .css( 'display', 'block' ); $panel .find( '.wpforms-gutenberg-panel-notice.wpforms-use-modern-notice' ) .css( 'display', 'none' ); return; } $panel .removeClass( 'disabled_panel' ) .find( '.wpforms-gutenberg-panel-notice.wpforms-lead-form-notice' ) .css( 'display', 'none' ); $panel .find( '.wpforms-gutenberg-panel-notice.wpforms-use-modern-notice' ) .css( 'display', null ); }, /** * Event `wpformsFormSelectorFormLoaded` handler. * * @since 1.8.1 * * @param {Object} e Event object. */ formLoaded( e ) { app.initLeadFormSettings( e.detail.block ); app.updateAccentColors( e.detail ); app.loadChoicesJS( e.detail ); app.initRichTextField( e.detail.formId ); app.initRepeaterField( e.detail.formId ); $( e.detail.block ) .off( 'click' ) .on( 'click', app.blockClick ); }, /** * Click on the block event handler. * * @since 1.8.1 * * @param {Object} e Event object. */ blockClick( e ) { app.initLeadFormSettings( e.currentTarget ); }, /** * Update accent colors of some fields in GB block in Modern Markup mode. * * @since 1.8.1 * * @param {Object} detail Event details object. */ updateAccentColors( detail ) { if ( ! wpforms_gutenberg_form_selector.is_modern_markup || ! window.WPForms?.FrontendModern || ! detail?.block ) { return; } const $form = $( detail.block.querySelector( `#wpforms-${ detail.formId }` ) ), FrontendModern = window.WPForms.FrontendModern; FrontendModern.updateGBBlockPageIndicatorColor( $form ); FrontendModern.updateGBBlockIconChoicesColor( $form ); FrontendModern.updateGBBlockRatingColor( $form ); }, /** * Init Modern style Dropdown fields (' ); } } formSubmitHandler( $form ); } ); }, /** * Handle 'action_required' server response. * * @param {object} e Event object. * @param {object} json Data returned form a server. * * @since 1.8.2 */ handleCardActionCallback: function( e, json ) { const $form = $( this ); if ( json.success && json.data.action_required ) { app.stripe.handleCardPayment( json.data.payment_intent_client_secret, { // eslint-disable-next-line camelcase payment_method: json.data.payment_method_id, } ).then( function( result ) { app.handleCardPaymentCallback( $form, result ); } ); } }, /** * Callback for Stripe 'handleCardPayment' method. * * @param {jQuery} $form Form element. * @param {object} result Data returned by 'handleCardPayment'. * * @since 1.8.2 */ handleCardPaymentCallback: function( $form, result ) { if ( result.error ) { app.formAjaxUnblock( $form ); $form.find( '.wpforms-field-stripe-credit-card-cardnumber' ).addClass( wpforms_stripe.data.element_classes.invalid ); app.displayStripeError( $form, result.error.message ); } else if ( result.paymentIntent && 'succeeded' === result.paymentIntent.status ) { $form.find( '.wpforms-stripe-payment-method-id' ).remove(); $form.find( '.wpforms-stripe-payment-intent-id' ).remove(); $form.append( '' ); wpforms.formSubmitAjax( $form ); } else { app.formAjaxUnblock( $form ); } }, /** * Display a field error using jQuery Validate library. * * @param {jQuery} $form Form element. * @param {object} message Error message. * * @since 1.8.2 */ displayStripeError: function( $form, message ) { const fieldName = $form.find( '.wpforms-stripe-credit-card-hidden-input' ).attr( 'name' ), $stripeDiv = $form.find( '.wpforms-field-stripe-credit-card-cardnumber' ); let errors = {}; errors[fieldName] = message; wpforms.displayFormAjaxFieldErrors( $form, errors ); // Switch page for the multipage form. if ( ! $stripeDiv.is( ':visible' ) && $form.find( '.wpforms-page-indicator-steps' ).length > 0 ) { // Empty $json object needed to change the page to the first one. wpforms.setCurrentPage( $form, {} ); } wpforms.scrollToError( $stripeDiv ); }, /** * Unblock the AJAX form. * * @since 1.8.2 * * @param {jQuery} $form Form element. */ formAjaxUnblock: function( $form ) { let $container = $form.closest( '.wpforms-container' ), $spinner = $form.find( '.wpforms-submit-spinner' ), $submit = $form.find( '.wpforms-submit' ), submitText = $submit.data( 'submit-text' ); if ( submitText ) { $submit.text( submitText ); } $submit.prop( 'disabled', false ); $container.css( 'opacity', '' ); $spinner.hide(); }, /** * Callback for a page changing. * * @since 1.8.2 * * @param {Event} event Event. * @param {int} currentPage Current page. * @param {jQuery} $form Current form. * @param {string} action The navigation action. */ pageChange: function( event, currentPage, $form, action ) { const $stripeDiv = $form.find( '.wpforms-field-stripe-credit-card-cardnumber' ), ccComplete = $stripeDiv.hasClass( wpforms_stripe.data.element_classes.complete ), ccEmpty = $stripeDiv.hasClass( wpforms_stripe.data.element_classes.empty ), ccInvalid = $stripeDiv.hasClass( wpforms_stripe.data.element_classes.invalid ); // Stop navigation through page break pages. if ( ! $stripeDiv.is( ':visible' ) || ( ! $stripeDiv.data( 'required' ) && ccEmpty ) || ( app.lockedPageToSwitch && app.lockedPageToSwitch !== currentPage ) || action === 'prev' ) { return; } if ( ccComplete ) { $stripeDiv.find( '.wpforms-error' ).remove(); return; } app.lockedPageToSwitch = currentPage; event.preventDefault(); if ( ccInvalid ) { return; } app.displayStripeError( $form, wpforms_stripe.i18n.empty_details ); }, /** * Get CSS property value. * In case of exception return empty string. * * @since 1.8.6 * * @param {jQuery} $element Element. * @param {string} property Property. * * @return {string} Property value. */ getCssPropertyValue( $element, property ) { try { return $element.css( property ); } catch ( e ) { return ''; } }, /** * Update Card Element styles in Modern Markup mode. * * @since 1.8.2 * * @param {jQuery} $form Form object. */ updateCardElementStylesModern( $form ) { // Should work only in Modern Markup mode. if ( ! window.WPForms || ! WPForms.FrontendModern || ! $.isEmptyObject( wpforms_stripe.data.element_style ) ) { return; } if ( ! $form || $form.length === 0 ) { return; } $form.find( '.wpforms-stripe-credit-card-hidden-input' ).each( function() { const $hiddenInput = $( this ); const cardElement = $hiddenInput.data( 'stripe-element' ); const inputStyle = { fontSize: app.getCssPropertyValue( $hiddenInput, 'font-size' ), colorText: app.getCssPropertyValue( $hiddenInput, 'color' ), }; if ( ! cardElement ) { return; } const styles = { base: { color: inputStyle.colorText, fontSize: inputStyle.fontSize, '::placeholder': { color: WPForms.FrontendModern.getColorWithOpacity( inputStyle.colorText, '0.5' ), fontSize: inputStyle.fontSize, }, }, invalid: { color: inputStyle.colorText, }, }; cardElement.update( { style: styles } ); } ); }, }; // Provide access to public functions/properties. return app; }( document, window, jQuery ) ); // Initialize. WPFormsStripeElements.init(); integrations/stripe/wpforms-stripe-elements.min.js000064400000012600151716470030016516 0ustar00"use strict";var WPFormsStripeElements=window.WPFormsStripeElements||function(n,t,o){const l={stripe:null,lockedPageToSwitch:0,init:function(){l.stripe=Stripe(wpforms_stripe.publishable_key,{locale:wpforms_stripe.data.element_locale}),o(n).on("wpformsReady",function(){o(".wpforms-stripe form").filter((e,t)=>"number"==typeof o(t).data("formid")).each(l.setupStripeForm)}),o(n).on("wpformsBeforePageChange",l.pageChange)},setupStripeForm:function(){var e=o(this);l.updateFormSubmitHandler(e),e.on("wpformsAjaxSubmitActionRequired",l.handleCardActionCallback),l.updateCardElementStylesModern(e)},setupCardElement:function(r,i){const s=r.find(".wpforms-stripe-credit-card-hidden-input");if(s&&0!==s.length){var t=s.data("stripe-element");if(!t){let e=wpforms_stripe.data.element_style;o.isEmptyObject(e)&&(e=l.getElementStyleDefault(s));var a={classes:wpforms_stripe.data.element_classes,hidePostalCode:!0,style:e};(t=l.stripe.elements().create("card",a)).mount(r.find(".wpforms-field-stripe-credit-card-cardnumber").get(0)),t.on("change",function(t){if(t.error){let e=t.error.message;"incomplete_number"!==t.error.code&&"invalid_number"!==t.error.code||(e=wpforms_settings.val_creditcard),l.displayStripeError(r,e)}else i.hideThese(i.errorsFor(s.get(0)))}),t.on("focus",function(){o(n).trigger("wpformsStripePaymentElementFocus",[r])}),s.data("stripe-element",t)}return t}},getElementStyleDefault:function(t){if(t&&0!==t.length){var r=t.css("color"),i=t.css("font-size"),i={base:{fontSize:i,color:r,"::placeholder":{color:r,fontSize:i}},invalid:{color:r}};let e=t.css("font-family");r=/[“”<>!@$%^&*=~`|{}[\]]/;return!r.test(e)&&-1===e.indexOf("MS Shell Dlg")||(e=o("p").css("font-family")),r.test(e)||(i.base.fontFamily=e,i.base["::placeholder"].fontFamily=e),i}},updateFormSubmitHandler:function(a){let e=a.validate(),n=e.settings.submitHandler,o=l.setupCardElement(a,e),d=a.find(".wpforms-field-stripe-credit-card-cardnumber");e.settings.submitHandler=function(){let e=a.validate().form(),t=d.hasClass(wpforms_stripe.data.element_classes.empty),r=d.data("required"),i=d.closest(".wpforms-field-stripe-credit-card").hasClass("wpforms-conditional-hide"),s=i?!1:r||!t&&!r;if(e&&s)a.find(".wpforms-submit").prop("disabled",!0),l.createPaymentMethod(a,o,r,n);else{if(e)return a.find(".wpforms-submit").prop("disabled",!1),n(a);a.find(".wpforms-submit").prop("disabled",!1),a.validate().cancelSubmit=!0}}},createPaymentMethod:function(t,e,r,i){l.stripe.createPaymentMethod("card",e,{billing_details:{name:t.find(".wpforms-field-stripe-credit-card-cardname").val()}}).then(function(e){e.error&&r?(t.find(".wpforms-submit").prop("disabled",!1),l.displayStripeError(t,e.error.message),t.validate().cancelSubmit=!0):(e.error||(t.find(".wpforms-stripe-payment-method-id").remove(),e.paymentMethod&&t.append('')),i(t))})},handleCardActionCallback:function(e,t){const r=o(this);t.success&&t.data.action_required&&l.stripe.handleCardPayment(t.data.payment_intent_client_secret,{payment_method:t.data.payment_method_id}).then(function(e){l.handleCardPaymentCallback(r,e)})},handleCardPaymentCallback:function(e,t){t.error?(l.formAjaxUnblock(e),e.find(".wpforms-field-stripe-credit-card-cardnumber").addClass(wpforms_stripe.data.element_classes.invalid),l.displayStripeError(e,t.error.message)):t.paymentIntent&&"succeeded"===t.paymentIntent.status?(e.find(".wpforms-stripe-payment-method-id").remove(),e.find(".wpforms-stripe-payment-intent-id").remove(),e.append(''),wpforms.formSubmitAjax(e)):l.formAjaxUnblock(e)},displayStripeError:function(e,t){var r=e.find(".wpforms-stripe-credit-card-hidden-input").attr("name"),i=e.find(".wpforms-field-stripe-credit-card-cardnumber"),s={};s[r]=t,wpforms.displayFormAjaxFieldErrors(e,s),!i.is(":visible")&&0' ); $form.append( '' ); wpforms.formSubmitAjax( $form ); return; } app.formAjaxUnblock( $form ); }, /** * Setup, mount and configure Stripe Payment Element. * * @since 1.8.2 * * @param {jQuery} $form Form element. */ setupPaymentElement( $form ) { const formId = $form.data( 'formid' ); if ( $.isEmptyObject( app.forms ) ) { app.initializeFormDefaultObject( formId ); } if ( app.forms[ formId ].paymentElement ) { return; } app.forms[ formId ].elements = app.stripe.elements( { currency: wpforms.getCurrency().code.toLowerCase(), mode: 'payment', // eslint-disable-next-line // See min amount for different currencies https://stripe.com/docs/currencies#minimum-and-maximum-charge-amounts. amount: 7777777, loader: 'always', locale: wpforms_stripe.data.element_locale, appearance: app.getElementAppearanceOptions( $form ), } ); app.initializePaymentElement( $form ); app.linkEmailMappedFieldTriggers( $form ); // Update the total amount in case of fixed price. wpforms.amountTotalCalc( $form ); // Update styles in Modern Markup mode. app.updatePaymentElementStylesModern( $form ); WPFormsUtils.triggerEvent( $( document ), 'wpformsStripePaymentElementInitialized', [ $form, app.forms ] ); }, /** * Handle Process Conditionals for Stripe field. * * @since 1.8.2 * * @param {string} formID Form ID. * @param {string} fieldID Field ID. * @param {boolean} pass Pass logic. * @param {string} action Action to execute. */ processConditionalsField( formID, fieldID, pass, action ) { // eslint-disable-line complexity const $form = $( '#wpforms-form-' + formID ), $stripeDiv = $form.find( '.wpforms-field-stripe-credit-card' ), isHidden = ( pass && action === 'hide' ) || ( ! pass && action !== 'hide' ); const forms = app.forms[ formID ] || []; const paymentElement = forms.paymentElement || null; if ( ! $stripeDiv.length || $stripeDiv.data( 'field-id' ).toString() !== fieldID || paymentElement || isHidden ) { return; } app.setupPaymentElement( $form ); }, /** * Get Element appearance options. * * @since 1.8.2 * * @param {jQuery} $form Form element. * * @return {Object} Appearance options. */ getElementAppearanceOptions( $form ) { // eslint-disable-line complexity, max-lines-per-function const customAppearanceOptions = app.getCustomAppearanceOptions(); if ( ! $.isEmptyObject( customAppearanceOptions ) ) { return customAppearanceOptions; } const $hiddenInput = $form.find( '.wpforms-stripe-credit-card-hidden-input' ), $fieldRow = $form.find( '.wpforms-field-stripe-credit-card .wpforms-field-row' ); const labelHide = ! $fieldRow.hasClass( 'wpforms-sublabel-hide' ); const colorPrimary = app.getElementPrimaryColor( $hiddenInput ); const customVars = app.getCustomAppearanceVariables( $form ); const inputStyle = { borderColor: app.getCssPropertyValue( $hiddenInput, '--field-border' ) || app.getCssPropertyValue( $hiddenInput, 'border-color' ), borderRadius: app.getCssPropertyValue( $hiddenInput, 'border-radius' ), fontSize: app.getCssPropertyValue( $hiddenInput, 'font-size' ), colorText: app.getCssPropertyValue( $hiddenInput, '--secondary-color' ) || app.getCssPropertyValue( $hiddenInput, 'color' ), colorTextPlaceholder: app.getCssPropertyValue( $hiddenInput, '--secondary-color-50' ) || WPFormsUtils.cssColorsUtils.getColorWithOpacity( app.getCssPropertyValue( $hiddenInput, 'color' ), '0.5' ), colorBackground: app.getCssPropertyValue( $hiddenInput, '--background-color' ) || app.getCssPropertyValue( $hiddenInput, 'background-color' ), fontFamily: app.getCssPropertyValue( $hiddenInput, 'font-family' ), errorColor: '#990000', }; inputStyle.colorBackground = WPFormsUtils.cssColorsUtils.rgbaToHex( inputStyle.colorBackground ); inputStyle.borderColor = WPFormsUtils.cssColorsUtils.isValidColor( inputStyle.borderColor ) ? inputStyle.borderColor : inputStyle.colorText; // We shouldn't provide opacity if we show sub-labels // since Stripe using this property for floating labels. const labelOpacity = ! labelHide ? { opacity: 0 } : {}; return { theme: 'stripe', labels: $fieldRow.data( 'sublabel-position' ), variables: { colorPrimary, colorBackground: inputStyle.colorBackground, colorText: inputStyle.colorText, colorDanger: inputStyle.errorColor, fontFamily: inputStyle.fontFamily, spacingUnit: '4px', spacingGridRow: '8px', fontSizeSm: '13px', fontWeightNormal: '400', borderRadius: inputStyle.borderRadius, colorTextPlaceholder: inputStyle.colorTextPlaceholder, colorIcon: inputStyle.colorText, logoColor: 'light', }, rules: { '.Input--invalid': { color: inputStyle.colorText, borderColor: '#cc0000', }, '.Input:disabled': { backgroundColor: inputStyle.colorBackground, borderColor: 'unset', }, '.Input': { border: 'none', borderRadius: inputStyle.borderRadius, boxShadow: '0 0 0 1px ' + inputStyle.borderColor, fontSize: inputStyle.fontSize, padding: '12px 14px', lineHeight: parseInt( inputStyle.fontSize, 10 ) + 5 + 'px', // match the font and line height to prevent overflow transition: 'none', color: inputStyle.colorText, backgroundColor: inputStyle.colorBackground, }, '.Input:focus, .Input:hover': { border: 'none', boxShadow: '0 0 0 2px ' + customVars.focusColor, outline: 'none', }, '.Label': { fontFamily: inputStyle.fontFamily, lineHeight: labelHide ? '1.3' : '0', color: colorPrimary, }, '.Label, .Label--floating': labelOpacity, '.CheckboxInput, .CodeInput, .PickerItem': { border: '1px solid ' + inputStyle.borderColor, }, [ app.getPickerItemSelectors().join( ', ' ) ]: { color: colorPrimary, boxShadow: 'none', borderColor: inputStyle.borderColor, backgroundColor: inputStyle.colorBackground, }, '.Block': { border: '1px solid ' + inputStyle.borderColor, borderRadius: inputStyle.borderRadius, }, '.Tab': { color: inputStyle.colorText, }, '.TabLabel, .TabIcon': { color: inputStyle.colorText, }, '.Tab--selected': { borderColor: '#999999', color: inputStyle.colorText, }, '.Action': { marginLeft: '6px', }, '.Action, .MenuAction': { border: 'none', backgroundColor: 'transparent', }, '.Action:hover, .MenuAction:hover': { border: 'none', backgroundColor: 'transparent', }, '.Error, .RedirectText': { color: inputStyle.errorColor, }, '.TabIcon--selected': { fill: inputStyle.colorText, }, '.AccordionItem': { border: 0, boxShadow: 'none', }, }, }; }, /** * Get custom appearance options. * * @since 1.8.5 * * @return {Object} Element appearance options. */ getCustomAppearanceOptions() { if ( typeof window.wpformsStripePaymentElementAppearance === 'object' ) { return window.wpformsStripePaymentElementAppearance; } if ( ! $.isEmptyObject( wpforms_stripe.data.element_appearance ) ) { return wpforms_stripe.data.element_appearance; } return {}; }, /** * Get CSS property value. * In case of exception, return empty string. * * @since 1.8.4 * * @param {jQuery} $element Element. * @param {string} property Property. * * @return {string} Property value. */ getCssPropertyValue( $element, property ) { try { return $element.css( property ); } catch ( e ) { return ''; } }, /** * Initialize Payment Element. * * @since 1.8.2 * * @param {jQuery} $form Form element. * @param {string} email Email address. */ initializePaymentElement( $form, email = '' ) { const $fieldRow = $form.find( '.wpforms-field-stripe-credit-card .wpforms-field-row' ); const formId = $form.data( 'formid' ); if ( app.forms[ formId ].paymentElement ) { app.forms[ formId ].paymentElement.destroy(); } app.forms[ formId ].paymentElement = app.forms[ formId ].elements.create( 'payment', { defaultValues : { billingDetails: { email } } } ); app.mountPaymentElement( $form ); // eslint-disable-next-line complexity app.forms[ formId ].paymentElement.on( 'change', function( event ) { app.forms[ formId ].paymentType = event.value.type; // Destroy a link element as it's not required for Google and Apple Pay. if ( ! $fieldRow.data( 'link-email' ) ) { if ( event.value.type === 'google_pay' || event.value.type === 'apple_pay' ) { app.forms[ formId ].linkElement.destroy(); app.forms[ formId ].linkDestroyed = true; } else if ( app.forms[ formId ].linkDestroyed ) { app.initializeLinkAuthenticationElement( $form ); app.forms[ formId ].linkDestroyed = false; } } $fieldRow.data( 'type', event.value.type ); if ( event.empty ) { $fieldRow.data( 'completed', false ); $fieldRow.find( 'label.wpforms-error' ).toggle( event.value.type === 'card' ); return; } app.forms[ formId ].elementsModified = true; if ( event.complete ) { $fieldRow.data( 'completed', true ); app.hideStripeFieldError( $form ); return; } $fieldRow.data( 'completed', false ); } ); app.forms[ formId ].paymentElement.on( 'loaderror', function( event ) { app.displayStripeLoadError( $form, event.error.message ); } ); // Trigger event when payment element is focused. app.forms[ formId ].paymentElement.on( 'focus', function() { app.triggerPaymentElementFocusEvent( $form ); } ); }, /** * Trigger Payment Element Focus Event. * * @since 1.9.3 * * @param {jQuery} $form Form element. */ triggerPaymentElementFocusEvent( $form ) { $( document ).trigger( 'wpformsStripePaymentElementFocus', [ $form ] ); }, /** * Mount Payment Element. * * @since 1.8.2 * * @param {jQuery} $form Form element. */ mountPaymentElement( $form ) { const formId = $form.data( 'formid' ), paymentRowId = `#wpforms-field-stripe-payment-element-${ formId }`; app.forms[ formId ].paymentElement.mount( paymentRowId ); }, /** * Link Email mapped field triggers. * * @since 1.8.2 * * @param {jQuery} $form Form element. */ linkEmailMappedFieldTriggers( $form ) { const $fieldRow = $form.find( '.wpforms-field-stripe-credit-card .wpforms-field-row' ); const linkEmailMappedField = app.getMappedLinkEmailField( $form ); if ( ! linkEmailMappedField ) { $fieldRow.data( 'linkCompleted', false ); app.initializeLinkAuthenticationElement( $form ); return; } const formId = $form.data( 'formid' ); linkEmailMappedField.on( 'change', function() { app.forms[ formId ].linkEmail = $( this ).val(); if ( $fieldRow.data( 'completed' ) ) { return; } // Reinitialize payment element if card data not completed yet. app.initializePaymentElement( $form, $( this ).val() ); } ); }, /** * Get Link Email mapped field. * * @since 1.8.2 * * @param {jQuery} $form Form element. * * @return {jQuery} Linked email field. */ getMappedLinkEmailField( $form ) { const linkEmailMappedFieldId = $form.find( '.wpforms-field-stripe-credit-card .wpforms-field-row' ).data( 'link-email' ); if ( ! linkEmailMappedFieldId ) { return null; } const formId = $form.data( 'formid' ); return $( `#wpforms-${ formId }-field_${ linkEmailMappedFieldId }` ); }, /** * Initialize Link Authentication Element. * * @since 1.8.2 * * @param {jQuery} $form Form element. */ initializeLinkAuthenticationElement( $form ) { const $fieldRow = $form.find( '.wpforms-field-stripe-credit-card .wpforms-field-row' ); const formId = $form.data( 'formid' ); app.forms[ formId ].linkElement = app.forms[ formId ].elements.create( 'linkAuthentication' ); app.mountLinkElement( $form ); app.forms[ formId ].linkElement.on( 'change', function( event ) { if ( event.empty ) { return; } app.forms[ formId ].elementsModified = true; if ( ! event.complete ) { $fieldRow.data( 'linkCompleted', false ); return; } if ( typeof event.value.email !== 'undefined' ) { app.forms[ formId ].linkEmail = event.value.email; } $fieldRow.data( 'linkCompleted', true ); app.hideStripeFieldError( $form ); } ); app.forms[ formId ].linkElement.on( 'loaderror', function( event ) { app.displayStripeLoadError( $form, event.error.message ); } ); // Trigger event when link element is focused. app.forms[ formId ].linkElement.on( 'focus', function() { app.triggerPaymentElementFocusEvent( $form ); } ); }, /** * Mount Payment Element. * * @since 1.8.2 * * @param {jQuery} $form Form element. */ mountLinkElement( $form ) { const formId = $form.data( 'formid' ), linkRowId = `#wpforms-field-stripe-link-element-${ formId }`; app.forms[ formId ].linkElement.mount( linkRowId ); }, /** * Update submitHandler for the forms containing Stripe. * * @since 1.8.2 * * @param {Object} form JS form element. */ // eslint-disable-next-line complexity submitHandler( form ) { const $form = $( form ), $stripeDiv = $form.find( '.wpforms-field-stripe-credit-card' ), $stripeRow = $stripeDiv.find( '.wpforms-field-row' ); const valid = $form.validate().form(), formId = $form.data( 'formid' ), ccRequired = $stripeRow.data( 'required' ), mobilePayment = [ 'google_pay', 'apple_pay' ].indexOf( app.forms[ formId ].paymentType ) !== -1, cardFilled = ( ! $stripeRow.data( 'link-email' ) && app.forms[ formId ].elementsModified ) || $stripeRow.data( 'completed' ) || mobilePayment; let processCard = false; if ( ! $stripeDiv.hasClass( 'wpforms-conditional-hide' ) ) { processCard = ccRequired || ( cardFilled && ! ccRequired ); } if ( valid && processCard ) { $form.find( '.wpforms-submit' ).prop( 'disabled', true ); $form.find( '.wpforms-submit-spinner' ).show(); app.createPaymentMethod( $form ); return; } if ( valid ) { originalSubmitHandler( $form ); return; } $form.find( '.wpforms-submit' ).prop( 'disabled', false ); $form.validate().cancelSubmit = true; }, /** * Update Elements total amount. * * @since 1.8.2 * * @param {Object} e Event object. * @param {jQuery} $form Form element. * @param {string} total Form total amount. */ updateElementsTotalAmount( e, $form, total ) { if ( ! total ) { return; } const formId = $form.data( 'formid' ); // Check if Stripe Elements exist on the form. // Can be in a multiple-step form or when the field is hidden by conditional logic. if ( ! app.forms[ formId ] || ! app.forms[ formId ].elements ) { return; } const currency = wpforms.getCurrency(); // Save total to variable to avoid calling `amountTotalCalc` again in SubmitHandler. app.forms[ formId ].total = total; app.forms[ formId ].elements.update( { amount: parseInt( wpforms.numberFormat( total, currency.decimals, '', '' ), 10 ) } ); }, /** * Confirm a setup payment. * * @param {jQuery} $form Form element. */ async createPaymentMethod( $form ) { const formId = $form.data( 'formid' ); if ( ! app.forms[ formId ].total ) { originalSubmitHandler( $form ); return; } await app.stripe.createPaymentMethod( { elements: app.forms[ formId ].elements, } ).then( function( result ) { if ( result.error ) { // eslint-disable-next-line prefer-const const basicErrors = [ 'incomplete_email', 'email_invalid', 'incomplete_number', 'invalid_number', 'incomplete_expiry', 'invalid_expiry_year_past', 'invalid_expiry_year', 'incomplete_cvc', 'incomplete_name', 'incomplete_phone_number', 'empty_phone_number', 'invalid_postal_code', ], message = basicErrors.includes( result.error.code ) ? '' : result.error.message; app.displayStripeFieldError( $form, message ); return; } app.forms[ formId ].paymentMethodId = result.paymentMethod.id; $form.append( '' ); originalSubmitHandler( $form ); } ); }, /** * Unblock the AJAX form. * * @since 1.8.2 * * @param {jQuery} $form Form element. */ formAjaxUnblock( $form ) { const $submit = $form.find( '.wpforms-submit' ); const submitText = $submit.data( 'submit-text' ); if ( submitText ) { $submit.text( submitText ); } $submit.prop( 'disabled', false ); $submit.removeClass( 'wpforms-disabled' ); $form.closest( '.wpforms-container' ).css( 'opacity', '' ); $form.find( '.wpforms-submit-spinner' ).hide(); }, /** * Display a generic Stripe Errors. * * @param {jQuery} $form Form element. * @param {string} message Error message. * * @since 1.8.2 */ displayStripeError( $form, message ) { wpforms.clearFormAjaxGeneralErrors( $form ); wpforms.displayFormAjaxErrors( $form, message ); app.formAjaxUnblock( $form ); }, /** * Display a field error using jQuery Validate library. * * @param {jQuery} $form Form element. * @param {string} message Error message. * * @since 1.8.2 */ displayStripeFieldError( $form, message ) { const fieldName = $form.find( '.wpforms-stripe-credit-card-hidden-input' ).attr( 'name' ), $stripeDiv = $form.find( '.wpforms-field-stripe-credit-card' ), errors = {}; if ( message ) { errors[ fieldName ] = message; } wpforms.displayFormAjaxFieldErrors( $form, errors ); // Switch page for the multipage form. if ( ! $stripeDiv.is( ':visible' ) && $form.find( '.wpforms-page-indicator-steps' ).length > 0 ) { // Empty $json object needed to change the page to the first one. wpforms.setCurrentPage( $form, {} ); } wpforms.scrollToError( $stripeDiv ); app.formAjaxUnblock( $form ); }, /** * Hide a field error. * * @param {jQuery} $form Form element. * * @since 1.8.2.3 */ hideStripeFieldError( $form ) { $form.find( '.wpforms-field-stripe-credit-card .wpforms-error' ).hide(); }, /** * Display a Stripe Elements load error. * * @since 1.8.2 * * @param {jQuery} $form Form element. * @param {string} message Error message. */ displayStripeLoadError( $form, message ) { message = wpforms_stripe.i18n.element_load_error + '
    ' + message; app.displayStripeError( $form, message ); }, /** * Callback for `wpformsBeforePageChange`. * * @since 1.8.2 * * @param {Event} event Event. * @param {number} currentPage Current page. * @param {jQuery} $form Current form. * @param {string} action The navigation action. */ pageChange( event, currentPage, $form, action ) { // eslint-disable-line complexity const $stripeDiv = $form.find( '.wpforms-field-stripe-credit-card .wpforms-field-row' ); const formId = $form.data( 'formid' ); if ( ! $stripeDiv.length || [ 'card', 'link' ].indexOf( app.forms[ formId ].paymentType ) === -1 ) { return; } if ( ! app.forms[ formId ].elementsModified && app.forms[ formId ].paymentType === 'card' ) { app.forms[ formId ].paymentElement.unmount(); app.mountPaymentElement( $form ); if ( ! $stripeDiv.data( 'link-email' ) ) { app.forms[ formId ].linkElement.unmount(); app.mountLinkElement( $form ); } } // Stop navigation through page break pages. if ( ! $stripeDiv.is( ':visible' ) || ( ! $stripeDiv.data( 'required' ) && ! app.forms[ formId ].elementsModified ) || ( app.forms[ formId ].lockedPageToSwitch && app.forms[ formId ].lockedPageToSwitch !== currentPage ) || action === 'prev' ) { return; } const linkCompleted = typeof $stripeDiv.data( 'linkCompleted' ) !== 'undefined' ? $stripeDiv.data( 'linkCompleted' ) : true; if ( $stripeDiv.data( 'completed' ) && linkCompleted ) { app.hideStripeFieldError( $form ); return; } app.forms[ formId ].lockedPageToSwitch = currentPage; app.displayStripeFieldError( $form, wpforms_stripe.i18n.empty_details ); event.preventDefault(); }, /** * Callback for `wpformsConvFormsFieldActivationAfter`. * * @since 1.8.2 * * @param {Event} event Event. * @param {Object} field CF field object. */ convFormsFieldActivationAfter( event, field ) { app.setupPaymentElement( field.$el.closest( 'form' ) ); }, /** * Get CSS variable value. * * @since 1.8.2 * @deprecated 1.8.8 * * @param {Object} style Computed style object. * @param {string} varName Style custom property name. * * @return {string} CSS variable value; */ // eslint-disable-next-line no-unused-vars getCssVar( style, varName ) { // eslint-disable-next-line no-console console.warn( 'WARNING! Function "WPFormsStripePaymentElement.getCssVar()" has been deprecated, please use the "WPForms.FrontendModern.getCssVar()" function instead!' ); return WPForms?.FrontendModern?.getCssVar(); }, /** * Update Payment Element styles in Modern Markup mode. * * @since 1.8.2 * * @param {jQuery} $form Form object. */ // eslint-disable-next-line complexity updatePaymentElementStylesModern( $form ) { // Should work only in Modern Markup mode. if ( ! $.isEmptyObject( app.getCustomAppearanceOptions() ) || ! window.WPForms || ! WPForms.FrontendModern ) { return; } if ( ! $form || $form.length === 0 ) { return; } // Skip Lead Form. if ( $form.closest( '.wpforms-container' ).hasClass( 'wpforms-lead-forms-container' ) ) { return; } const formId = $form.data( 'formid' ); if ( ! app.forms[ formId ] ) { return; } const formElements = app.forms[ formId ].elements; const cssVars = WPForms.FrontendModern.getCssVars( $form ); app.updateFormElementsAppearance( formElements, cssVars ); }, /** * Update Payment Elements appearance with given CSS variables data. * * @since 1.8.2 * * @param {Object} formElements Form payment elements. * @param {Object} formElements._commonOptions Form payment elements common options. * @param {Function} formElements.update Form payment elements common options. * @param {Object} cssVars CSS variables data. */ updateFormElementsAppearance( formElements, cssVars ) { // eslint-disable-line max-lines-per-function if ( ! formElements || ! formElements._commonOptions ) { return; } // Get existing appearance options. const appearance = formElements._commonOptions.appearance; // Need to perform vertical padding calculation. cssVars[ 'field-size-padding-v' ] = ( ( parseInt( cssVars[ 'field-size-input-height' ], 10 ) - parseInt( cssVars[ 'field-size-font-size' ], 10 ) - 6 ) / 2 ) + 'px'; // Update variables. appearance.variables.spacingGridRow = cssVars[ 'field-size-input-spacing' ]; appearance.variables.spacingGridColumn = '20px'; appearance.variables.spacingTab = '10px'; appearance.variables.colorText = WPForms.FrontendModern.getSolidColor( cssVars[ 'field-text-color' ] ); let maybeMenuBgColor = ! WPFormsUtils.cssColorsUtils.isTransparentColor( cssVars[ 'field-background-color' ] ) ? cssVars[ 'field-background-color' ] : cssVars[ 'field-menu-color' ]; maybeMenuBgColor = WPForms.FrontendModern.getSolidColor( maybeMenuBgColor ); // Update rules. appearance.rules = { '.Input': { border: cssVars[ 'field-border-size' ] + ' ' + cssVars[ 'field-border-style' ] + ' ' + cssVars[ 'field-border-color' ], borderRadius: cssVars[ 'field-border-radius' ], padding: `${ cssVars[ 'field-size-padding-v' ] } ${ cssVars[ 'field-size-padding-h' ] }`, fontSize: cssVars[ 'field-size-font-size' ], lineHeight: cssVars[ 'field-size-font-size' ], backgroundColor: cssVars[ 'field-background-color' ], boxShadow: 'none', outline: 'none', }, '.Input:focus': { backgroundColor: maybeMenuBgColor, borderColor: cssVars[ 'button-background-color' ], borderStyle: 'solid', boxShadow: '0 0 0 1px ' + cssVars[ 'button-background-color' ], outline: 'none', }, '.Input--invalid': { borderColor: cssVars[ 'label-error-color' ], boxShadow: 'none', color: appearance.variables.colorText, outline: 'none', }, '.Input--invalid:focus': { borderColor: cssVars[ 'label-error-color' ], boxShadow: '0 0 0 1px ' + cssVars[ 'label-error-color' ], outline: 'none', }, '.Input:disabled': { ...appearance.rules[ '.Input:disabled' ], }, '.Input::placeholder': { color: WPForms.FrontendModern.getColorWithOpacity( cssVars[ 'field-text-color' ], '0.5' ), fontSize: cssVars[ 'field-size-font-size' ], }, '.CheckboxInput': { border: '1px solid ' + cssVars[ 'field-border-color' ], backgroundColor: cssVars[ 'field-background-color' ], }, '.CheckboxInput--checked': { borderColor: cssVars[ 'button-background-color' ], backgroundColor: cssVars[ 'button-background-color' ], }, '.CodeInput': { border: '1px solid ' + cssVars[ 'field-text-color' ], backgroundColor: maybeMenuBgColor, }, '.CodeInput:focus': { borderWidth: '2px', boxShadow: '0 0 0 1px ' + cssVars[ 'button-background-color' ], outline: 'none', }, '.Label': { fontSize: cssVars[ 'label-size-sublabel-font-size' ], margin: `0 0 ${ cssVars[ 'field-size-sublabel-spacing' ] } 0`, color: cssVars[ 'label-sublabel-color' ], lineHeight: appearance.rules[ '.Label' ].lineHeight, }, '.Label, .Label--floating': { ...appearance.rules[ '.Label, .Label--floating' ], }, '.Error': { fontSize: cssVars[ 'label-size-sublabel-font-size' ], margin: `${ cssVars[ 'field-size-sublabel-spacing' ] } 0 0 0`, color: cssVars[ 'label-error-color' ], }, '.Tab': { border: '1px solid ' + WPForms.FrontendModern.getColorWithOpacity( cssVars[ 'field-border-color' ], '0.5' ), borderRadius: cssVars[ 'field-border-radius' ], backgroundColor: 'transparent', boxShadow: 'none', marginTop: '0', }, '.Tab:focus': { border: '1px solid ' + WPForms.FrontendModern.getColorWithOpacity( cssVars[ 'button-background-color' ], '0.5' ), boxShadow: `0 0 0 3px ${ WPForms.FrontendModern.getColorWithOpacity( cssVars[ 'button-background-color' ], '0.25' ) }`, outline: 'none', }, '.Tab:hover': { border: `1px solid ${ cssVars[ 'field-border-color' ] }`, }, '.Tab--selected': { borderColor: cssVars[ 'button-background-color' ], boxShadow: `0 0 0 1px ${ cssVars[ 'button-background-color' ] }`, backgroundColor: cssVars[ 'field-background-color' ], }, '.Tab--selected:hover': { borderColor: cssVars[ 'button-background-color' ], }, '.Tab--selected:focus': { borderColor: cssVars[ 'button-background-color' ], boxShadow: `0 0 0 1px ${ cssVars[ 'button-background-color' ] }`, }, '.TabLabel': { color: cssVars[ 'field-text-color' ], }, '.TabIcon': { fill: WPForms.FrontendModern.getColorWithOpacity( cssVars[ 'field-text-color' ], '0.75' ), }, '.TabIcon--selected': { fill: cssVars[ 'field-text-color' ], }, '.TabIcon:hover': { color: cssVars[ 'field-text-color' ], fill: cssVars[ 'field-text-color' ], }, '.TabLabel--selected': { color: cssVars[ 'button-background-color' ], }, '.Block': { border: '1px solid ' + WPForms.FrontendModern.getColorWithOpacity( cssVars[ 'field-border-color' ], '0.5' ), backgroundColor: maybeMenuBgColor, borderRadius: cssVars[ 'field-border-radius' ], boxShadow: 'none', }, '.AccordionItem': { ...appearance.rules[ '.AccordionItem' ], backgroundColor: maybeMenuBgColor, paddingLeft: 0, paddingRight: 0, color: cssVars[ 'field-text-color' ], }, [ app.getPickerItemSelectors().join( ', ' ) ]: { border: 0, boxShadow: 'none', backgroundColor: maybeMenuBgColor, }, }; formElements.update( { appearance } ); }, /** * Retrieves the custom appearance variables for the given form. * * @since 1.9.4 * * @param {jQuery} $form The jQuery object representing the form element. * * @return {Object} An object containing custom appearance variables for the provided form. */ getCustomAppearanceVariables( $form ) { const formId = $form.data( 'formid' ); if ( app.forms[ formId ]?.customVars ) { return app.forms[ formId ].customVars; } const $hiddenInput = $form.find( '.wpforms-stripe-credit-card-hidden-input' ); const primaryColor = app.getElementPrimaryColor( $hiddenInput ); app.forms[ formId ].customVars = { focusColor: app.getCssPropertyValue( $hiddenInput, '--accent-color' ) || app.getCssPropertyValue( $hiddenInput, 'color' ), borderColorWithOpacity: WPFormsUtils.cssColorsUtils.getColorWithOpacity( primaryColor, '0.1' ), }; return app.forms[ formId ].customVars; }, /** * Get the primary color of the payment element. * * @since 1.9.4 * * @param {jQuery} $cardInput The input element for the payment card. * * @return {string} The primary color value. */ getElementPrimaryColor( $cardInput ) { const primaryColor = app.getCssPropertyValue( $cardInput, '--primary-color' ) || app.getCssPropertyValue( $cardInput, 'color' ); if ( ! window?.WPForms?.FrontendModern ) { return primaryColor; } return WPForms.FrontendModern.getSolidColor( primaryColor ); }, /** * Retrieves the CSS selectors for various states of picker items. * * @since 1.9.4 * * @return {Object} An object containing CSS selectors for different states of picker items, including default, hover, selected, and highlighted states. */ getPickerItemSelectors() { return [ '.PickerItem', '.PickerItem:hover', '.PickerItem--selected', '.PickerItem--selected:hover', '.PickerItem--highlight', '.PickerItem--highlight:hover', ]; }, }; // Provide access to public functions/properties. return app; }( document, window, jQuery ) ); // Initialize. WPFormsStripePaymentElement.init(); integrations/stripe/wpforms-stripe-payment-element.min.js000064400000042554151716470030020021 0ustar00var WPFormsStripePaymentElement=window.WPFormsStripePaymentElement||function(t,i,d){let s;const m={stripe:null,forms:{},init(){m.stripe=Stripe(wpforms_stripe.publishable_key,{locale:wpforms_stripe.data.element_locale,betas:["elements_enable_deferred_intent_beta_1"]}),d(t).on("wpformsReady",function(){d(".wpforms-stripe form").each(m.setupStripeForm).on("wpformsConvFormsFieldActivationAfter",m.convFormsFieldActivationAfter)}),d(t).on("wpformsBeforePageChange",m.pageChange).on("wpformsAmountTotalCalculated",m.updateElementsTotalAmount).on("wpformsProcessConditionalsField",function(e,o,r,t,n){m.processConditionalsField(o,r,t,n)})},initializeFormsDefaultObject(){console.warn('WARNING! Function "WPFormsStripePaymentElement.initializeFormsDefaultObject()" has been deprecated, please use the "WPFormsStripePaymentElement.initializeFormDefaultObject( formId )" function instead!'),d(".wpforms-stripe form").each(function(){m.initializeFormDefaultObject(d(this).data("formid"))})},initializeFormDefaultObject(e){m.forms[e]={elements:null,paymentElement:null,elementsModified:!1,linkElement:null,linkEmail:"",linkDestroyed:!1,paymentType:"",lockedPageToSwitch:0,paymentMethodId:"",total:"",customVars:null}},setupStripeForm(){var e,o,r=d(this);WPFormsUtils.triggerEvent(d(t),"wpformsBeforeStripePaymentElementSetup",[r]).isDefaultPrevented()||(e=r.data("formid"),void 0===m.forms[e]&&(m.initializeFormDefaultObject(e),(e=r.find(".wpforms-field-stripe-credit-card")).find(".wpforms-field-row").length)&&(o=r.data("validator"))&&(s=o.settings.submitHandler,o.settings.submitHandler=m.submitHandler,r.on("wpformsAjaxSubmitActionRequired",m.confirmPaymentActionCallback),e.hasClass("wpforms-conditional-field")||m.setupPaymentElement(r)))},async confirmPaymentActionCallback(e,o){if(o.success&&o.data.action_required){const n=d(this);var r=new URL(i.location.href),t=n.data("formid");await m.stripe.confirmPayment({clientSecret:o.data.payment_intent_client_secret,confirmParams:{return_url:r.toString(),payment_method:m.forms[t].paymentMethodId},redirect:"if_required"}).then(function(e){m.handleConfirmPayment(n,e)})}},handleConfirmPayment(e,o){var r;o.error?m.displayStripeError(e,o.error.message):(r=e.data("formid"),o.paymentIntent&&"succeeded"===o.paymentIntent.status?(e.find(".wpforms-stripe-payment-method-id").remove(),e.find(".wpforms-stripe-payment-intent-id").remove(),e.append(''),e.append(''),wpforms.formSubmitAjax(e)):m.formAjaxUnblock(e))},setupPaymentElement(e){var o=e.data("formid");d.isEmptyObject(m.forms)&&m.initializeFormDefaultObject(o),m.forms[o].paymentElement||(m.forms[o].elements=m.stripe.elements({currency:wpforms.getCurrency().code.toLowerCase(),mode:"payment",amount:7777777,loader:"always",locale:wpforms_stripe.data.element_locale,appearance:m.getElementAppearanceOptions(e)}),m.initializePaymentElement(e),m.linkEmailMappedFieldTriggers(e),wpforms.amountTotalCalc(e),m.updatePaymentElementStylesModern(e),WPFormsUtils.triggerEvent(d(t),"wpformsStripePaymentElementInitialized",[e,m.forms]))},processConditionalsField(e,o,r,t){var n=d("#wpforms-form-"+e),i=n.find(".wpforms-field-stripe-credit-card"),r=r&&"hide"===t||!r&&"hide"!==t,t=(m.forms[e]||[]).paymentElement||null;!i.length||i.data("field-id").toString()!==o||t||r||m.setupPaymentElement(n)},getElementAppearanceOptions(e){var o,r,t,n,i,l=m.getCustomAppearanceOptions();return d.isEmptyObject(l)?(n=e.find(".wpforms-stripe-credit-card-hidden-input"),r=!(o=e.find(".wpforms-field-stripe-credit-card .wpforms-field-row")).hasClass("wpforms-sublabel-hide"),t=m.getElementPrimaryColor(n),e=m.getCustomAppearanceVariables(e),(n={borderColor:m.getCssPropertyValue(n,"--field-border")||m.getCssPropertyValue(n,"border-color"),borderRadius:m.getCssPropertyValue(n,"border-radius"),fontSize:m.getCssPropertyValue(n,"font-size"),colorText:m.getCssPropertyValue(n,"--secondary-color")||m.getCssPropertyValue(n,"color"),colorTextPlaceholder:m.getCssPropertyValue(n,"--secondary-color-50")||WPFormsUtils.cssColorsUtils.getColorWithOpacity(m.getCssPropertyValue(n,"color"),"0.5"),colorBackground:m.getCssPropertyValue(n,"--background-color")||m.getCssPropertyValue(n,"background-color"),fontFamily:m.getCssPropertyValue(n,"font-family"),errorColor:"#990000"}).colorBackground=WPFormsUtils.cssColorsUtils.rgbaToHex(n.colorBackground),n.borderColor=WPFormsUtils.cssColorsUtils.isValidColor(n.borderColor)?n.borderColor:n.colorText,i=r?{}:{opacity:0},{theme:"stripe",labels:o.data("sublabel-position"),variables:{colorPrimary:t,colorBackground:n.colorBackground,colorText:n.colorText,colorDanger:n.errorColor,fontFamily:n.fontFamily,spacingUnit:"4px",spacingGridRow:"8px",fontSizeSm:"13px",fontWeightNormal:"400",borderRadius:n.borderRadius,colorTextPlaceholder:n.colorTextPlaceholder,colorIcon:n.colorText,logoColor:"light"},rules:{".Input--invalid":{color:n.colorText,borderColor:"#cc0000"},".Input:disabled":{backgroundColor:n.colorBackground,borderColor:"unset"},".Input":{border:"none",borderRadius:n.borderRadius,boxShadow:"0 0 0 1px "+n.borderColor,fontSize:n.fontSize,padding:"12px 14px",lineHeight:parseInt(n.fontSize,10)+5+"px",transition:"none",color:n.colorText,backgroundColor:n.colorBackground},".Input:focus, .Input:hover":{border:"none",boxShadow:"0 0 0 2px "+e.focusColor,outline:"none"},".Label":{fontFamily:n.fontFamily,lineHeight:r?"1.3":"0",color:t},".Label, .Label--floating":i,".CheckboxInput, .CodeInput, .PickerItem":{border:"1px solid "+n.borderColor},[m.getPickerItemSelectors().join(", ")]:{color:t,boxShadow:"none",borderColor:n.borderColor,backgroundColor:n.colorBackground},".Block":{border:"1px solid "+n.borderColor,borderRadius:n.borderRadius},".Tab":{color:n.colorText},".TabLabel, .TabIcon":{color:n.colorText},".Tab--selected":{borderColor:"#999999",color:n.colorText},".Action":{marginLeft:"6px"},".Action, .MenuAction":{border:"none",backgroundColor:"transparent"},".Action:hover, .MenuAction:hover":{border:"none",backgroundColor:"transparent"},".Error, .RedirectText":{color:n.errorColor},".TabIcon--selected":{fill:n.colorText},".AccordionItem":{border:0,boxShadow:"none"}}}):l},getCustomAppearanceOptions(){return"object"==typeof i.wpformsStripePaymentElementAppearance?i.wpformsStripePaymentElementAppearance:d.isEmptyObject(wpforms_stripe.data.element_appearance)?{}:wpforms_stripe.data.element_appearance},getCssPropertyValue(e,o){try{return e.css(o)}catch(e){return""}},initializePaymentElement(o,e=""){const r=o.find(".wpforms-field-stripe-credit-card .wpforms-field-row"),t=o.data("formid");m.forms[t].paymentElement&&m.forms[t].paymentElement.destroy(),m.forms[t].paymentElement=m.forms[t].elements.create("payment",{defaultValues:{billingDetails:{email:e}}}),m.mountPaymentElement(o),m.forms[t].paymentElement.on("change",function(e){m.forms[t].paymentType=e.value.type,r.data("link-email")||("google_pay"===e.value.type||"apple_pay"===e.value.type?(m.forms[t].linkElement.destroy(),m.forms[t].linkDestroyed=!0):m.forms[t].linkDestroyed&&(m.initializeLinkAuthenticationElement(o),m.forms[t].linkDestroyed=!1)),r.data("type",e.value.type),e.empty?(r.data("completed",!1),r.find("label.wpforms-error").toggle("card"===e.value.type)):(m.forms[t].elementsModified=!0,e.complete?(r.data("completed",!0),m.hideStripeFieldError(o)):r.data("completed",!1))}),m.forms[t].paymentElement.on("loaderror",function(e){m.displayStripeLoadError(o,e.error.message)}),m.forms[t].paymentElement.on("focus",function(){m.triggerPaymentElementFocusEvent(o)})},triggerPaymentElementFocusEvent(e){d(t).trigger("wpformsStripePaymentElementFocus",[e])},mountPaymentElement(e){var e=e.data("formid"),o="#wpforms-field-stripe-payment-element-"+e;m.forms[e].paymentElement.mount(o)},linkEmailMappedFieldTriggers(e){const o=e.find(".wpforms-field-stripe-credit-card .wpforms-field-row");var r=m.getMappedLinkEmailField(e);if(r){const t=e.data("formid");r.on("change",function(){m.forms[t].linkEmail=d(this).val(),o.data("completed")||m.initializePaymentElement(e,d(this).val())})}else o.data("linkCompleted",!1),m.initializeLinkAuthenticationElement(e)},getMappedLinkEmailField(e){var o=e.find(".wpforms-field-stripe-credit-card .wpforms-field-row").data("link-email");return o?(e=e.data("formid"),d(`#wpforms-${e}-field_`+o)):null},initializeLinkAuthenticationElement(o){const r=o.find(".wpforms-field-stripe-credit-card .wpforms-field-row"),t=o.data("formid");m.forms[t].linkElement=m.forms[t].elements.create("linkAuthentication"),m.mountLinkElement(o),m.forms[t].linkElement.on("change",function(e){e.empty||(m.forms[t].elementsModified=!0,e.complete?(void 0!==e.value.email&&(m.forms[t].linkEmail=e.value.email),r.data("linkCompleted",!0),m.hideStripeFieldError(o)):r.data("linkCompleted",!1))}),m.forms[t].linkElement.on("loaderror",function(e){m.displayStripeLoadError(o,e.error.message)}),m.forms[t].linkElement.on("focus",function(){m.triggerPaymentElementFocusEvent(o)})},mountLinkElement(e){var e=e.data("formid"),o="#wpforms-field-stripe-link-element-"+e;m.forms[e].linkElement.mount(o)},submitHandler(e){var e=d(e),o=e.find(".wpforms-field-stripe-credit-card"),r=o.find(".wpforms-field-row"),t=e.validate().form(),n=e.data("formid"),i=r.data("required"),l=-1!==["google_pay","apple_pay"].indexOf(m.forms[n].paymentType),n=!r.data("link-email")&&m.forms[n].elementsModified||r.data("completed")||l;let a=!1;o.hasClass("wpforms-conditional-hide")||(a=i||n&&!i),t&&a?(e.find(".wpforms-submit").prop("disabled",!0),e.find(".wpforms-submit-spinner").show(),m.createPaymentMethod(e)):t?s(e):(e.find(".wpforms-submit").prop("disabled",!1),e.validate().cancelSubmit=!0)},updateElementsTotalAmount(e,o,r){var t;r&&(o=o.data("formid"),m.forms[o])&&m.forms[o].elements&&(t=wpforms.getCurrency(),m.forms[o].total=r,m.forms[o].elements.update({amount:parseInt(wpforms.numberFormat(r,t.decimals,"",""),10)}))},async createPaymentMethod(r){const t=r.data("formid");m.forms[t].total?await m.stripe.createPaymentMethod({elements:m.forms[t].elements}).then(function(e){var o;e.error?(o=["incomplete_email","email_invalid","incomplete_number","invalid_number","incomplete_expiry","invalid_expiry_year_past","invalid_expiry_year","incomplete_cvc","incomplete_name","incomplete_phone_number","empty_phone_number","invalid_postal_code"].includes(e.error.code)?"":e.error.message,m.displayStripeFieldError(r,o)):(m.forms[t].paymentMethodId=e.paymentMethod.id,r.append(''),s(r))}):s(r)},formAjaxUnblock(e){var o=e.find(".wpforms-submit"),r=o.data("submit-text");r&&o.text(r),o.prop("disabled",!1),o.removeClass("wpforms-disabled"),e.closest(".wpforms-container").css("opacity",""),e.find(".wpforms-submit-spinner").hide()},displayStripeError(e,o){wpforms.clearFormAjaxGeneralErrors(e),wpforms.displayFormAjaxErrors(e,o),m.formAjaxUnblock(e)},displayStripeFieldError(e,o){var r=e.find(".wpforms-stripe-credit-card-hidden-input").attr("name"),t=e.find(".wpforms-field-stripe-credit-card"),n={};o&&(n[r]=o),wpforms.displayFormAjaxFieldErrors(e,n),!t.is(":visible")&&0 "+o,m.displayStripeError(e,o)},pageChange(e,o,r,t){var n=r.find(".wpforms-field-stripe-credit-card .wpforms-field-row"),i=r.data("formid");n.length&&-1!==["card","link"].indexOf(m.forms[i].paymentType)&&(m.forms[i].elementsModified||"card"!==m.forms[i].paymentType||(m.forms[i].paymentElement.unmount(),m.mountPaymentElement(r),n.data("link-email"))||(m.forms[i].linkElement.unmount(),m.mountLinkElement(r)),!n.is(":visible")||!n.data("required")&&!m.forms[i].elementsModified||m.forms[i].lockedPageToSwitch&&m.forms[i].lockedPageToSwitch!==o||"prev"===t||(t=void 0===n.data("linkCompleted")||n.data("linkCompleted"),n.data("completed")&&t?m.hideStripeFieldError(r):(m.forms[i].lockedPageToSwitch=o,m.displayStripeFieldError(r,wpforms_stripe.i18n.empty_details),e.preventDefault())))},convFormsFieldActivationAfter(e,o){m.setupPaymentElement(o.$el.closest("form"))},getCssVar(e,o){return console.warn('WARNING! Function "WPFormsStripePaymentElement.getCssVar()" has been deprecated, please use the "WPForms.FrontendModern.getCssVar()" function instead!'),WPForms?.FrontendModern?.getCssVar()},updatePaymentElementStylesModern(e){var o;d.isEmptyObject(m.getCustomAppearanceOptions())&&i.WPForms&&WPForms.FrontendModern&&e&&0!==e.length&&!e.closest(".wpforms-container").hasClass("wpforms-lead-forms-container")&&(o=e.data("formid"),m.forms[o])&&(o=m.forms[o].elements,e=WPForms.FrontendModern.getCssVars(e),m.updateFormElementsAppearance(o,e))},updateFormElementsAppearance(e,o){var r,t;e&&e._commonOptions&&(r=e._commonOptions.appearance,o["field-size-padding-v"]=(parseInt(o["field-size-input-height"],10)-parseInt(o["field-size-font-size"],10)-6)/2+"px",r.variables.spacingGridRow=o["field-size-input-spacing"],r.variables.spacingGridColumn="20px",r.variables.spacingTab="10px",r.variables.colorText=WPForms.FrontendModern.getSolidColor(o["field-text-color"]),t=WPFormsUtils.cssColorsUtils.isTransparentColor(o["field-background-color"])?o["field-menu-color"]:o["field-background-color"],t=WPForms.FrontendModern.getSolidColor(t),r.rules={".Input":{border:o["field-border-size"]+" "+o["field-border-style"]+" "+o["field-border-color"],borderRadius:o["field-border-radius"],padding:o["field-size-padding-v"]+" "+o["field-size-padding-h"],fontSize:o["field-size-font-size"],lineHeight:o["field-size-font-size"],backgroundColor:o["field-background-color"],boxShadow:"none",outline:"none"},".Input:focus":{backgroundColor:t,borderColor:o["button-background-color"],borderStyle:"solid",boxShadow:"0 0 0 1px "+o["button-background-color"],outline:"none"},".Input--invalid":{borderColor:o["label-error-color"],boxShadow:"none",color:r.variables.colorText,outline:"none"},".Input--invalid:focus":{borderColor:o["label-error-color"],boxShadow:"0 0 0 1px "+o["label-error-color"],outline:"none"},".Input:disabled":{...r.rules[".Input:disabled"]},".Input::placeholder":{color:WPForms.FrontendModern.getColorWithOpacity(o["field-text-color"],"0.5"),fontSize:o["field-size-font-size"]},".CheckboxInput":{border:"1px solid "+o["field-border-color"],backgroundColor:o["field-background-color"]},".CheckboxInput--checked":{borderColor:o["button-background-color"],backgroundColor:o["button-background-color"]},".CodeInput":{border:"1px solid "+o["field-text-color"],backgroundColor:t},".CodeInput:focus":{borderWidth:"2px",boxShadow:"0 0 0 1px "+o["button-background-color"],outline:"none"},".Label":{fontSize:o["label-size-sublabel-font-size"],margin:`0 0 ${o["field-size-sublabel-spacing"]} 0`,color:o["label-sublabel-color"],lineHeight:r.rules[".Label"].lineHeight},".Label, .Label--floating":{...r.rules[".Label, .Label--floating"]},".Error":{fontSize:o["label-size-sublabel-font-size"],margin:o["field-size-sublabel-spacing"]+" 0 0 0",color:o["label-error-color"]},".Tab":{border:"1px solid "+WPForms.FrontendModern.getColorWithOpacity(o["field-border-color"],"0.5"),borderRadius:o["field-border-radius"],backgroundColor:"transparent",boxShadow:"none",marginTop:"0"},".Tab:focus":{border:"1px solid "+WPForms.FrontendModern.getColorWithOpacity(o["button-background-color"],"0.5"),boxShadow:"0 0 0 3px "+WPForms.FrontendModern.getColorWithOpacity(o["button-background-color"],"0.25"),outline:"none"},".Tab:hover":{border:"1px solid "+o["field-border-color"]},".Tab--selected":{borderColor:o["button-background-color"],boxShadow:"0 0 0 1px "+o["button-background-color"],backgroundColor:o["field-background-color"]},".Tab--selected:hover":{borderColor:o["button-background-color"]},".Tab--selected:focus":{borderColor:o["button-background-color"],boxShadow:"0 0 0 1px "+o["button-background-color"]},".TabLabel":{color:o["field-text-color"]},".TabIcon":{fill:WPForms.FrontendModern.getColorWithOpacity(o["field-text-color"],"0.75")},".TabIcon--selected":{fill:o["field-text-color"]},".TabIcon:hover":{color:o["field-text-color"],fill:o["field-text-color"]},".TabLabel--selected":{color:o["button-background-color"]},".Block":{border:"1px solid "+WPForms.FrontendModern.getColorWithOpacity(o["field-border-color"],"0.5"),backgroundColor:t,borderRadius:o["field-border-radius"],boxShadow:"none"},".AccordionItem":{...r.rules[".AccordionItem"],backgroundColor:t,paddingLeft:0,paddingRight:0,color:o["field-text-color"]},[m.getPickerItemSelectors().join(", ")]:{border:0,boxShadow:"none",backgroundColor:t}},e.update({appearance:r}))},getCustomAppearanceVariables(e){var o,r=e.data("formid");return m.forms[r]?.customVars||(e=e.find(".wpforms-stripe-credit-card-hidden-input"),o=m.getElementPrimaryColor(e),m.forms[r].customVars={focusColor:m.getCssPropertyValue(e,"--accent-color")||m.getCssPropertyValue(e,"color"),borderColorWithOpacity:WPFormsUtils.cssColorsUtils.getColorWithOpacity(o,"0.1")}),m.forms[r].customVars},getElementPrimaryColor(e){e=m.getCssPropertyValue(e,"--primary-color")||m.getCssPropertyValue(e,"color");return i?.WPForms?.FrontendModern?WPForms.FrontendModern.getSolidColor(e):e},getPickerItemSelectors(){return[".PickerItem",".PickerItem:hover",".PickerItem--selected",".PickerItem--selected:hover",".PickerItem--highlight",".PickerItem--highlight:hover"]}};return m}(document,window,jQuery);WPFormsStripePaymentElement.init();integrations/woocommerce/notifications.js000064400000002545151716470030015012 0ustar00/* global wpforms_woocommerce_notifications */ /** * WooCommerce Notifications integration script. * * @since 1.8.9 */ const WPFormsWoocommerceNotifications = window.WPFormsWoocommerceNotifications || ( function( document, window, $ ) { /** * Public functions and properties. * * @since 1.8.9 */ const app = { /** * Start the engine. * * @since 1.8.9 */ init() { $( app.ready ); }, /** * Document ready. * * @since 1.8.9 */ ready() { app.events(); }, /** * Events. * * @since 1.8.9 */ events() { $( '#wpforms-woocommerce-close' ).on( 'click', app.dismiss ); }, /** * Hide notification. * * @since 1.8.9 */ dismiss() { const $btn = $( this ); const $notification = $btn.closest( '.wpforms-woocommerce-notification' ); $notification.remove(); const data = { action: 'wpforms_woocommerce_dismiss', nonce: wpforms_woocommerce_notifications.nonce, }; $.post( wpforms_woocommerce_notifications.ajax_url, data, function( res ) { if ( ! res.success ) { // eslint-disable-next-line no-console console.log( res ); } } ).fail( function( xhr ) { // eslint-disable-next-line no-console console.log( xhr.responseText ); } ); }, }; return app; }( document, window, jQuery ) ); // Initialize. WPFormsWoocommerceNotifications.init(); integrations/woocommerce/notifications.min.js000064400000001105151716470030015563 0ustar00const WPFormsWoocommerceNotifications=window.WPFormsWoocommerceNotifications||function(c){const o={init(){c(o.ready)},ready(){o.events()},events(){c("#wpforms-woocommerce-close").on("click",o.dismiss)},dismiss(){c(this).closest(".wpforms-woocommerce-notification").remove();var o={action:"wpforms_woocommerce_dismiss",nonce:wpforms_woocommerce_notifications.nonce};c.post(wpforms_woocommerce_notifications.ajax_url,o,function(o){o.success||console.log(o)}).fail(function(o){console.log(o.responseText)})}};return o}((document,window,jQuery));WPFormsWoocommerceNotifications.init();integrations/wpcode/wpcode.js000064400000006466151716470030012372 0ustar00/* global wpformsWpcodeVars, List, wpforms_admin */ /** * @param wpformsWpcodeVars.installing_text */ // noinspection ES6ConvertVarToLetConst /** * WPCode integration script. * * @since 1.8.5 */ // eslint-disable-next-line no-var var WPFormsWPCode = window.WPFormsWPCode || ( function( document, window, $ ) { /** * Public functions and properties. * * @since 1.8.5 */ const app = { /** * Blue spinner HTML. * * @since 1.8.5 * * @type {Object} */ spinnerBlue: '', /** * White spinner HTML. * * @since 1.8.5 * * @type {Object} */ spinnerWhite: '', /** * List.js object. * * @since 1.8.5 * * @type {Object} */ snippetSearch: null, /** * Start the engine. * * @since 1.8.5 */ init() { $( app.ready ); }, /** * Document ready. * * @since 1.8.5 */ ready() { app.snippetSearch = new List( 'wpforms-wpcode-snippets-list', { valueNames: [ 'wpforms-wpcode-snippet-title' ], } ); app.events(); }, /** * Events. * * @since 1.8.5 */ events() { $( '.wpforms-wpcode-snippet-button' ).on( 'click', app.installSnippet ); $( '.wpforms-wpcode-popup-button' ).on( 'click', app.installPlugin ); $( '#wpforms-wpcode-snippet-search' ).on( 'keyup search', function() { app.searchSnippet( this ); } ); }, /** * Install snippet. * * @since 1.8.5 */ installSnippet() { const $button = $( this ); if ( $button.data( 'action' ) === 'edit' ) { return; } const originalWidth = $button.width(); const $badge = $button.prev( '.wpforms-wpcode-snippet-badge' ); $badge.addClass( 'wpforms-wpcode-installing-in-progress' ).text( wpformsWpcodeVars.installing_text ); $button.width( originalWidth ).html( app.spinnerBlue ); }, /** * Search snippet. * * @param {Object} searchField The search field html element. * @since 1.8.5 */ searchSnippet( searchField ) { const searchTerm = $( searchField ).val(); const searchResults = app.snippetSearch.search( searchTerm ); const $noResultsMessage = $( '#wpforms-wpcode-no-results' ); if ( searchResults.length === 0 ) { $noResultsMessage.show(); } else { $noResultsMessage.hide(); } }, /** * Install or activate WPCode plugin by button click. * * @since 1.8.5 */ installPlugin() { const $btn = $( this ); if ( $btn.hasClass( 'disabled' ) ) { return; } const action = $btn.attr( 'data-action' ), plugin = $btn.attr( 'data-plugin' ), // eslint-disable-next-line camelcase args = JSON.stringify( { overwrite_package: true } ), ajaxAction = action === 'activate' ? 'wpforms_activate_addon' : 'wpforms_install_addon'; // Fix original button width, add spinner and disable it. $btn.width( $btn.width() ).html( app.spinnerWhite ).addClass( 'disabled' ); const data = { action: ajaxAction, nonce: wpforms_admin.nonce, plugin, args, type: 'plugin', }; $.post( wpforms_admin.ajax_url, data ) .done( function() { location.reload(); } ); }, }; return app; }( document, window, jQuery ) ); // Initialize. WPFormsWPCode.init(); integrations/wpcode/wpcode.min.js000064400000002715151716470030013145 0ustar00var WPFormsWPCode=window.WPFormsWPCode||function(t){const p={spinnerBlue:'',spinnerWhite:'',snippetSearch:null,init(){t(p.ready)},ready(){p.snippetSearch=new List("wpforms-wpcode-snippets-list",{valueNames:["wpforms-wpcode-snippet-title"]}),p.events()},events(){t(".wpforms-wpcode-snippet-button").on("click",p.installSnippet),t(".wpforms-wpcode-popup-button").on("click",p.installPlugin),t("#wpforms-wpcode-snippet-search").on("keyup search",function(){p.searchSnippet(this)})},installSnippet(){var n,i=t(this);"edit"!==i.data("action")&&(n=i.width(),i.prev(".wpforms-wpcode-snippet-badge").addClass("wpforms-wpcode-installing-in-progress").text(wpformsWpcodeVars.installing_text),i.width(n).html(p.spinnerBlue))},searchSnippet(n){var n=t(n).val(),n=p.snippetSearch.search(n),i=t("#wpforms-wpcode-no-results");0===n.length?i.show():i.hide()},installPlugin(){var n,i,e,s=t(this);s.hasClass("disabled")||(e=s.attr("data-action"),n=s.attr("data-plugin"),i=JSON.stringify({overwrite_package:!0}),e="activate"===e?"wpforms_activate_addon":"wpforms_install_addon",s.width(s.width()).html(p.spinnerWhite).addClass("disabled"),s={action:e,nonce:wpforms_admin.nonce,plugin:n,args:i,type:"plugin"},t.post(wpforms_admin.ajax_url,s).done(function(){location.reload()}))}};return p}((document,window,jQuery));WPFormsWPCode.init();share/utils.js000064400000015712151716470030007356 0ustar00 // noinspection ES6ConvertVarToLetConst // eslint-disable-next-line no-var, no-unused-vars var WPFormsUtils = window.WPFormsUtils || ( function( document, window, $ ) { /** * Public functions and properties. * * @since 1.7.6 * * @type {Object} */ const app = { /** * Wrapper to trigger a native or custom event and return the event object. * * @since 1.7.6 * * @param {jQuery} $element Element to trigger event on. * @param {string} eventName Event name to trigger (custom or native). * @param {Array} args Trigger arguments. * * @return {Event} Event object. */ triggerEvent( $element, eventName, args = [] ) { const eventObject = new $.Event( eventName ); $element.trigger( eventObject, args ); return eventObject; }, /** * Debounce. * * This function comes directly from underscore.js: * * Returns a function, that, as long as it continues to be invoked, will not * be triggered. The function will be called after it stops being called for * N milliseconds. If `immediate` is passed, trigger the function on the * leading edge, instead of the trailing. * * Debouncing is removing unwanted input noise from buttons, switches or other user input. * Debouncing prevents extra activations or slow functions from triggering too often. * * @param {Function} func The function to be debounced. * @param {number} wait The amount of time to delay calling func. * @param {boolean} immediate Whether or not to trigger the function on the leading edge. * * @return {Function} Returns a function that, as long as it continues to be invoked, will not be triggered. */ debounce( func, wait, immediate ) { let timeout; return function() { const context = this, args = arguments; const later = function() { timeout = null; if ( ! immediate ) { func.apply( context, args ); } }; const callNow = immediate && ! timeout; clearTimeout( timeout ); timeout = setTimeout( later, wait ); if ( callNow ) { func.apply( context, args ); } }; }, /** * CSS color operations. * * @since 1.8.8 * * @type {Object} */ cssColorsUtils: { /** * Checks if the provided color has transparency. * * @since 1.8.8 * * @param {string} color The color to check. * @param {number} opacityThreshold The max opacity value of the color that is considered as transparent. * * @return {boolean} Returns true if the color is transparent. */ isTransparentColor( color, opacityThreshold = 0.33 ) { const rgba = app.cssColorsUtils.getColorAsRGBArray( color ); const opacity = Number( rgba?.[ 3 ] ); // Compare the opacity value with the threshold. return opacity <= opacityThreshold; }, /** * Get color as an array of RGB(A) values. * * @since 1.8.8 * * @param {string} color Color. * * @return {Array|boolean} Color as an array of RGBA values. False on error. */ getColorAsRGBArray( color ) { // Check if the given color is a valid CSS color. if ( ! app.cssColorsUtils.isValidColor( color ) ) { return false; } // Remove # from the beginning of the string and remove whitespaces. color = color.replace( /^#/, '' ).replaceAll( ' ', '' ); color = color === 'transparent' ? 'rgba(0,0,0,0)' : color; const rgba = color; let rgbArray; // Check if color is in HEX(A) format. const isHex = rgba.match( /[0-9a-f]{6,8}$/ig ); if ( isHex ) { // Search and split HEX(A) color into an array of couples of chars. rgbArray = rgba.match( /\w\w/g ).map( ( x ) => parseInt( x, 16 ) ); rgbArray[ 3 ] = rgbArray[ 3 ] || rgbArray[ 3 ] === 0 ? ( rgbArray[ 3 ] / 255 ).toFixed( 2 ) : 1; } else { rgbArray = rgba.split( '(' )[ 1 ].split( ')' )[ 0 ].split( ',' ); } return rgbArray; }, /** * Check if the given color is a valid CSS color. * * @since 1.8.8 * * @param {string} color Color. * * @return {boolean} True if the given color is a valid CSS color. */ isValidColor( color ) { // Create a temporary DOM element and use `style` property. const s = new Option().style; s.color = color; // Invalid color leads to the empty color property of DOM element style. return s.color !== ''; }, /** * Get contrast color relative to given color. * * @since 1.8.8 * * @param {string} color Color. * * @return {string} True if the given color is a valid CSS color. */ getContrastColor( color ) { const rgba = app.cssColorsUtils.getColorAsRGBArray( color ); const sum = rgba.reduce( ( a, b ) => a + b, 0 ); const avg = Math.round( ( sum / 3 ) * ( rgba[ 3 ] ?? 1 ) ); return avg < 128 ? '#ffffff' : '#000000'; }, /** * Add opacity to color string. * Supports formats: RGB, RGBA, HEX, HEXA. * * If the given color has an alpha channel, the new alpha channel will be calculated according to the given opacity. * * @since 1.8.9 * * @param {string} color Color. * @param {string} opacity Opacity. * * @return {string} Color in RGBA format with an added alpha channel according to given opacity. */ getColorWithOpacity( color, opacity ) { color = color.trim(); const rgbArray = app.cssColorsUtils.getColorAsRGBArray( color ); if ( ! rgbArray ) { return color; } // Default opacity is 1. opacity = ! opacity || opacity.length === 0 ? '1' : opacity.toString(); const alpha = rgbArray.length === 4 ? parseFloat( rgbArray[ 3 ] ) : 1; // Calculate new alpha value. const newAlpha = parseFloat( opacity ) * alpha; // Combine and return the RGBA color. return `rgba(${ rgbArray[ 0 ] },${ rgbArray[ 1 ] },${ rgbArray[ 2 ] },${ newAlpha })`.replace( /\s+/g, '' ); }, /** * Convert an RGBA color string to HEX format. * * @since 1.9.4 * * @param {string} color Color in "rgba(r, g, b, a)" or "rgb(r, g, b)" format. * * @return {false|string} HEX color. */ rgbaToHex( color ) { if ( ! /^rgb/.test( color ) ) { return color; } const rgbArray = app.cssColorsUtils.getColorAsRGBArray( color ); if ( ! rgbArray ) { return false; } const red = Number( rgbArray[ 0 ] ); const green = Number( rgbArray[ 1 ] ); const blue = Number( rgbArray[ 2 ] ); const alpha = rgbArray[ 3 ] ? Math.round( Number( rgbArray[ 3 ] ) * 255 ) : 255; // Ensure numbers are converted to valid two-character hexadecimal strings. const colorToHex = ( value ) => value.toString( 16 ).padStart( 2, '0' ); // Convert to hex and return as a single string. const hex = `#${ [ colorToHex( red ), colorToHex( green ), colorToHex( blue ), alpha < 255 ? colorToHex( alpha ) : '', ].join( '' ) }`; return hex.toLowerCase(); }, }, }; // Provide access to public functions/properties. return app; }( document, window, jQuery ) ); share/utils.min.js000064400000002734151716470030010140 0ustar00var WPFormsUtils=window.WPFormsUtils||function(e){const a={triggerEvent(r,t,o=[]){t=new e.Event(t);return r.trigger(t,o),t},debounce(e,s,l){let a;return function(){const r=this,t=arguments;var o=l&&!a;clearTimeout(a),a=setTimeout(function(){a=null,l||e.apply(r,t)},s),o&&e.apply(r,t)}},cssColorsUtils:{isTransparentColor(r,t=.33){r=a.cssColorsUtils.getColorAsRGBArray(r);return Number(r?.[3])<=t},getColorAsRGBArray(r){if(!a.cssColorsUtils.isValidColor(r))return!1;r="transparent"===(r=r.replace(/^#/,"").replaceAll(" ",""))?"rgba(0,0,0,0)":r;let t;return r.match(/[0-9a-f]{6,8}$/gi)?(t=r.match(/\w\w/g).map(r=>parseInt(r,16)))[3]=t[3]||0===t[3]?(t[3]/255).toFixed(2):1:t=r.split("(")[1].split(")")[0].split(","),t},isValidColor(r){var t=(new Option).style;return t.color=r,""!==t.color},getContrastColor(r){var r=a.cssColorsUtils.getColorAsRGBArray(r),t=r.reduce((r,t)=>r+t,0);return Math.round(t/3*(r[3]??1))<128?"#ffffff":"#000000"},getColorWithOpacity(r,t){r=r.trim();var o=a.cssColorsUtils.getColorAsRGBArray(r);if(!o)return r;t=t&&0!==t.length?t.toString():"1";r=4===o.length?parseFloat(o[3]):1,t=parseFloat(t)*r;return`rgba(${o[0]},${o[1]},${o[2]},${t})`.replace(/\s+/g,"")},rgbaToHex(r){var t,o,e,s,l;return/^rgb/.test(r)?!!(s=a.cssColorsUtils.getColorAsRGBArray(r))&&(t=Number(s[0]),o=Number(s[1]),e=Number(s[2]),s=s[3]?Math.round(255*Number(s[3])):255,("#"+[(l=r=>r.toString(16).padStart(2,"0"))(t),l(o),l(e),s<255?l(s):""].join("")).toLowerCase()):r}}};return a}((document,window,jQuery));cmb2-char-counter.js000064400000012013151717007610010317 0ustar00/** * Used for character counters */ window.CMB2 = window.CMB2 || {}; window.CMB2.charcounter = window.CMB2.charcounter || {}; ( function(window, document, $, cmb, counter ) { 'use strict'; if ( ! wp.utils || ! wp.utils.WordCounter ) { return cmb.log( 'Cannot find wp.utils!' ); } // Private variables counter.counters = {}; var counters = counter.counters; var wpCounter = new wp.utils.WordCounter(); /** * Update a field's character counter * * @since 2.7.0 * * @param {string} field_id * * @return {int} */ counter.updateCounter = function( field_id ) { // No counter? if ( ! counters.hasOwnProperty( field_id ) ) { return null; } var instance = counters[ field_id ]; var wysiwyg = instance.editor && ! instance.editor.isHidden(); // Are we dealing with WYSIWYG visual editor, or textarea / WYSIWYG textarea? var text = wysiwyg ? instance.editor.getContent( { format: 'raw' } ) : cmb.$id( field_id ).val().trim(); var count = wpCounter.count( text, instance.type ); var exceeded = instance.max && count > instance.max; // Number remaining when max is defined var val = instance.max ? instance.max - count : count; // Over maximum? instance.$el.parents( '.cmb2-char-counter-wrap' )[ exceeded ? 'addClass' : 'removeClass' ]( 'cmb2-max-exceeded' ); // Update counter, and update counter input width. instance.$el.val( val ).outerWidth( ( ( 8 * String( val ).length ) + 15 ) + 'px' ); return count; }; counter.instantiate = function( $el ) { var data = $el.data(); // Add counter details if not already done if ( ! ( data.fieldId in counters ) ) { var instance = { $el : $el, max : data.max, type : 'words' === data.counterType ? 'words' : 'characters_including_spaces', editor : false, }; counters[ data.fieldId ] = instance; // Initialise counter counter.updateCounter( data.fieldId ); } }; /** * Initializes all character counters. Hooked to cmb_init. * * @since 2.7.0 * * @param {bool} init First init? * * @return {void} */ counter.initAll = function() { // Gather counters and initialise $( '.cmb2-char-counter' ).each( function() { counter.instantiate( $( this ) ); }); }; /** * Initializes WYSIWYG editors. Hooked to tinymce-editor-init * * @since 2.7.0 * * @param {object} evt * @param {object} editor * * @return {void} */ counter.initWysiwyg = function( evt, editor ) { // Check if it's one of our WYSIWYGs // Should have already been registered in counters via hidden textarea if ( editor.id in counters ) { // Add editor to counter counters[ editor.id ].editor = editor; // Add nodechange event editor.on( 'nodechange keyup', counter.countWysiwyg ); } }; /** * Initializes after a new repeatable row has been added. Hooked to cmb2_add_row * * @since 2.7.0 * * @param {object} evt A jQuery-normalized event object. * @param {object} $row A jQuery dom element object for the group row. * * @return {void} */ counter.addRow = function( evt, $row ) { // Character counters in row? $row.find( '.cmb2-char-counter' ).each( function() { // Update attributes var $this = $( this ); var id = $this.attr( 'id' ); var field_id = id.replace( /^char-counter-/, '' ); $this.attr( 'data-field-id', field_id ).data( 'field-id', field_id ); counter.instantiate( $this ); }); }; /** * Clean the counters array. * Removes counters after a repeatable row has been removed. Hooked to cmb2_remove_row. * * @since 2.7.0 * * @return {void} */ counter.cleanCounters = function() { var field_id, remove = []; // Got through counters for ( field_id in counters ) { // Check for element, gather for removal if ( ! document.getElementById( field_id ) ) { remove.push( field_id ); } } // Anything to remove? if ( remove.length ) { _.each( remove, function( field_id ) { delete counters[ field_id ]; }); } }; /** * Counts the value of wysiwyg on the keyup event. * * @since 2.7.0 * * @param {object} evt * * @return {void} */ counter.countWysiwyg = _.throttle( function( evt ) { // Init event if ( evt.hasOwnProperty( 'element' ) ) { return counter.updateCounter( $( evt.element ).data( 'id' ) ); } // Nodechange event if ( evt.hasOwnProperty( 'currentTarget' ) ) { return counter.updateCounter( $( evt.currentTarget ).data( 'id' ) ); } } ); /** * Counts the value of textarea on the keyup event. * * @since 2.7.0 * * @param {object} evt * * @return {void} */ counter.countTextarea = _.throttle( function(evt) { counter.updateCounter( evt.currentTarget.id ); }, 400 ); // Hook in our event callbacks. $( document ) .on( 'cmb_init', counter.initAll ) .on( 'tinymce-editor-init', counter.initWysiwyg ) .on( 'cmb2_add_row', counter.addRow ) .on( 'cmb2_remove_row', counter.cleanCounters ) .on( 'input keyup', '.cmb2-count-chars', counter.countTextarea ); } )( window, document, jQuery, window.CMB2, window.CMB2.charcounter ); cmb2-wysiwyg.js000064400000023256151717007610007462 0ustar00/** * Used for WYSIWYG logic */ window.CMB2 = window.CMB2 || {}; window.CMB2.wysiwyg = window.CMB2.wysiwyg || {}; ( function(window, document, $, cmb, wysiwyg, undefined ) { 'use strict'; // Private variables var toBeDestroyed = []; var toBeInitialized = []; var all = wysiwyg.all = {}; // Private functions /** * Initializes any editors that weren't initialized because they didn't exist yet. * * @since 2.2.3 * * @return {void} */ function delayedInit() { // Don't initialize until they've all been destroyed. if ( 0 === toBeDestroyed.length ) { toBeInitialized.forEach( function ( toInit ) { toBeInitialized.splice( toBeInitialized.indexOf( toInit ), 1 ); wysiwyg.init.apply( wysiwyg, toInit ); } ); } else { window.setTimeout( delayedInit, 100 ); } } /** * Destroys any editors that weren't destroyed because they didn't exist yet. * * @since 2.2.3 * * @return {void} */ function delayedDestroy() { toBeDestroyed.forEach( function( id ) { toBeDestroyed.splice( toBeDestroyed.indexOf( id ), 1 ); wysiwyg.destroy( id ); } ); } /** * Gets the option data for a group (and initializes that data if it doesn't exist). * * @since 2.2.3 * * @param {object} data The group/field data. * * @return {object} Options data object for a group. */ function getGroupData( data ) { var groupid = data.groupid; var fieldid = data.fieldid; if ( ! all[ groupid ] || ! all[ groupid ][ fieldid ] ) { all[ groupid ] = all[ groupid ] || {}; all[ groupid ][ fieldid ] = { template : wp.template( 'cmb2-wysiwyg-' + groupid + '-' + fieldid ), defaults : { // Get the data from the template-wysiwyg initiation. mce : $.extend( {}, tinyMCEPreInit.mceInit[ 'cmb2_i_' + groupid + fieldid ] ), qt : $.extend( {}, tinyMCEPreInit.qtInit[ 'cmb2_i_' + groupid + fieldid ] ) } }; // This is the template-wysiwyg data, and we do not want that to be initiated. delete tinyMCEPreInit.mceInit[ 'cmb2_i_' + groupid + fieldid ]; delete tinyMCEPreInit.qtInit[ 'cmb2_i_' + groupid + fieldid ]; } return all[ groupid ][ fieldid ]; } /** * Initiates the tinyMCEPreInit options for a wysiwyg editor instance. * * @since 2.2.3 * * @param {object} options Options data object for the wysiwyg editor instance. * * @return {void} */ function initOptions( options ) { var nameRegex = new RegExp( 'cmb2_n_' + options.groupid + options.fieldid, 'g' ); var idRegex = new RegExp( 'cmb2_i_' + options.groupid + options.fieldid, 'g' ); var prop, newSettings, newQTS; // If no settings for this field. Clone from placeholder. if ( 'undefined' === typeof( tinyMCEPreInit.mceInit[ options.id ] ) ) { newSettings = $.extend( {}, options.defaults.mce ); for ( prop in newSettings ) { if ( 'string' === typeof( newSettings[ prop ] ) ) { newSettings[ prop ] = newSettings[ prop ] .replace( idRegex, options.id ) .replace( nameRegex, options.name ); } } tinyMCEPreInit.mceInit[ options.id ] = newSettings; } // If no Quicktag settings for this field. Clone from placeholder. if ( 'undefined' === typeof( tinyMCEPreInit.qtInit[ options.id ] ) ) { newQTS = $.extend( {}, options.defaults.qt ); for ( prop in newQTS ) { if ( 'string' === typeof( newQTS[ prop ] ) ) { newQTS[ prop ] = newQTS[ prop ] .replace( idRegex, options.id ) .replace( nameRegex, options.name ); } } tinyMCEPreInit.qtInit[ options.id ] = newQTS; } } /** * Initializes all group wysiwyg editors. Hooked to cmb_init. * * @since 2.2.3 * * @return {void} */ wysiwyg.initAll = function() { var $this,data,initiated; $( '.cmb2-wysiwyg-placeholder' ).each( function() { $this = $( this ); data = $this.data(); if ( data.groupid ) { data.id = $this.attr( 'id' ); data.name = $this.attr( 'name' ); data.value = $this.val(); wysiwyg.init( $this, data, false ); initiated = true; } } ); if ( true === initiated ) { if ( 'undefined' !== typeof window.QTags ) { window.QTags._buttonsInit(); } // Hook in our event callbacks. $( document ) .on( 'cmb2_add_row', wysiwyg.addRow ) .on( 'cmb2_remove_group_row_start', wysiwyg.destroyRowEditors ) .on( 'cmb2_shift_rows_start', wysiwyg.shiftStart ) .on( 'cmb2_shift_rows_complete', wysiwyg.shiftComplete ); } }; /** * Initiates wysiwyg editors in a new group row. Hooked to cmb2_add_row. * * @since 2.2.3 * * @param {object} evt A jQuery-normalized event object. * @param {object} $row A jQuery dom element object for the group row. * * @return {void} */ wysiwyg.addRow = function( evt, $row ) { wysiwyg.initRow( $row, evt ); }; /** * Destroys wysiwyg editors in a group row when that row is removed. Hooked to cmb2_remove_group_row_start. * * @since 2.2.3 * * @param {object} evt A jQuery-normalized event object. * @param {object} $btn A jQuery dom element object for the remove-row button. * * @return {void} */ wysiwyg.destroyRowEditors = function( evt, $btn ) { wysiwyg.destroy( $btn.parents( '.cmb-repeatable-grouping' ).find( '.wp-editor-area' ).attr( 'id' ) ); }; /** * When a row-shift starts, we need to destroy the wysiwyg editors for the group-rows being shuffled. * * @since 2.2.3 * * @param {object} evt A jQuery-normalized event object. * @param {object} $btn A jQuery dom element object for the remove-row button. * @param {object} $from A jQuery dom element object for the row being shifted from. * @param {object} $to A jQuery dom element object for the row being shifted to. * * @return {void} */ wysiwyg.shiftStart = function( evt, $btn, $from, $to ) { $from.add( $to ).find( '.wp-editor-wrap textarea' ).each( function() { wysiwyg.destroy( $( this ).attr( 'id' ) ); } ); }; /** * When a row-shift completes, we need to re-init the wysiwyg editors for the group-rows being shuffled. * * @since 2.2.3 * * @param {object} evt A jQuery-normalized event object. * @param {object} $btn A jQuery dom element object for the remove-row button. * @param {object} $from A jQuery dom element object for the row being shifted from. * @param {object} $to A jQuery dom element object for the row being shifted to. * * @return {void} */ wysiwyg.shiftComplete = function( evt, $btn, $from, $to ) { $from.add( $to ).each( function() { wysiwyg.initRow( $( this ), evt ); } ); }; /** * Initializes editors for a new CMB row. * * @since 2.2.3 * * @param {object} $row A jQuery dom element object for the group row. * @param {object} evt A jQuery-normalized event object. * * @return {void} */ wysiwyg.initRow = function( $row, evt ) { var $toReplace, data, defVal; $row.find( '.cmb2-wysiwyg-inner-wrap' ).each( function() { $toReplace = $( this ); data = $toReplace.data(); defVal = cmb.getFieldArg( data.hash, 'default', '' ); defVal = 'undefined' !== typeof defVal && false !== defVal ? defVal : ''; data.iterator = $row.data( 'iterator' ); data.fieldid = data.id; data.id = data.groupid + '_' + data.iterator + '_' + data.fieldid; data.name = data.groupid + '[' + data.iterator + '][' + data.fieldid + ']'; data.value = 'cmb2_add_row' !== evt.type && $toReplace.find( '.wp-editor-area' ).length ? $toReplace.find( '.wp-editor-area' ).val() : defVal; // The destroys might not have happened yet. Don't init until they have. if ( 0 === toBeDestroyed.length ) { wysiwyg.init( $toReplace, data ); } else { toBeInitialized.push( [$toReplace, data] ); window.setTimeout( delayedInit, 100 ); } } ); }; /** * Initiates a wysiwyg editor instance and replaces the passed dom element w/ the editor html. * * @since 2.2.3 * * @param {object} $toReplace A jQuery dom element which will be replaced with the wysiwyg editor. * @param {object} data Data used to initate the editor. * @param {bool} buttonsInit Whether to run QTags._buttonsInit() * * @return {void} */ wysiwyg.init = function( $toReplace, data, buttonsInit ) { if ( ! data.groupid ) { return false; } var mceActive = cmb.canTinyMCE(); var qtActive = 'function' === typeof window.quicktags; $.extend( data, getGroupData( data ) ); initOptions( data ); $toReplace.replaceWith( data.template( data ) ); if ( mceActive ) { window.tinyMCE.init( tinyMCEPreInit.mceInit[ data.id ] ); } if ( qtActive ) { window.quicktags( tinyMCEPreInit.qtInit[ data.id ] ); } if ( mceActive ) { $( document.getElementById( data.id ) ).parents( '.wp-editor-wrap' ).removeClass( 'html-active' ).addClass( 'tmce-active' ); } if ( false !== buttonsInit && 'undefined' !== typeof window.QTags ) { window.QTags._buttonsInit(); } }; /** * Destroys a wysiwyg editor instance. * * @since 2.2.3 * * @param {string} id Editor id. * * @return {void} */ wysiwyg.destroy = function( id ) { if ( ! cmb.canTinyMCE() ) { // Nothing to see here. return; } // The editor might not be initialized yet. But we need to destroy it once it is. var editor = tinyMCE.get( id ); if ( editor !== null && typeof( editor ) !== 'undefined' ) { editor.destroy(); if ( 'undefined' === typeof( tinyMCEPreInit.mceInit[ id ] ) ) { delete tinyMCEPreInit.mceInit[ id ]; } if ( 'undefined' === typeof( tinyMCEPreInit.qtInit[ id ] ) ) { delete tinyMCEPreInit.qtInit[ id ]; } } else if ( -1 === toBeDestroyed.indexOf( id ) ) { toBeDestroyed.push( id ); window.setTimeout( delayedDestroy, 100 ); } }; // Hook in our event callbacks. $( document ).on( 'cmb_init', wysiwyg.initAll ); } )( window, document, jQuery, window.CMB2, window.CMB2.wysiwyg ); cmb2.js000064400000115542151717007610005742 0ustar00/** * Controls the behaviours of custom metabox fields. * * @author CMB2 team * @see https://github.com/CMB2/CMB2 */ /** * Custom jQuery for Custom Metaboxes and Fields */ window.CMB2 = window.CMB2 || {}; (function(window, document, $, cmb, undefined){ 'use strict'; // localization strings var l10n = window.cmb2_l10; var setTimeout = window.setTimeout; var $document; var $id = function( selector ) { return $( document.getElementById( selector ) ); }; var getRowId = function( id, newIterator ) { id = id.split('-'); id.splice(id.length - 1, 1); id.push( newIterator ); return id.join('-'); }; cmb.$id = $id; var defaults = { idNumber : false, repeatEls : 'input:not([type="button"]),select,textarea,.cmb2-media-status', noEmpty : 'input:not([type="button"]):not([type="radio"]):not([type="checkbox"]),textarea', repeatUpdate : 'input:not([type="button"]),select,textarea,label', styleBreakPoint : 450, mediaHandlers : {}, defaults : { time_picker : l10n.defaults.time_picker, date_picker : l10n.defaults.date_picker, color_picker : l10n.defaults.color_picker || {}, code_editor : l10n.defaults.code_editor, }, media : { frames : {}, }, }; cmb.init = function() { $document = $( document ); // Setup the CMB2 object defaults. $.extend( cmb, defaults ); cmb.trigger( 'cmb_pre_init' ); var $metabox = cmb.metabox(); var $repeatGroup = $metabox.find('.cmb-repeatable-group'); // Init time/date/color pickers cmb.initPickers( $metabox.find('input[type="text"].cmb2-timepicker'), $metabox.find('input[type="text"].cmb2-datepicker'), $metabox.find('input[type="text"].cmb2-colorpicker') ); // Init code editors. cmb.initCodeEditors( $metabox.find( '.cmb2-textarea-code:not(.disable-codemirror)' ) ); // Insert toggle button into DOM wherever there is multicheck. credit: Genesis Framework $( '

    ' + l10n.strings.check_toggle + '

    ' ).insertBefore( '.cmb2-checkbox-list:not(.no-select-all)' ); // Make File List drag/drop sortable: cmb.makeListSortable(); // Make Repeatable fields drag/drop sortable: cmb.makeRepeatableSortable(); $metabox .on( 'change', '.cmb2_upload_file', function() { cmb.media.field = $( this ).attr( 'id' ); $id( cmb.media.field + '_id' ).val(''); }) // Media/file management .on( 'click', '.cmb-multicheck-toggle', cmb.toggleCheckBoxes ) .on( 'click', '.cmb2-upload-button', cmb.handleMedia ) .on( 'click', '.cmb-attach-list li, .cmb2-media-status .img-status img, .cmb2-media-status .file-status > span', cmb.handleFileClick ) .on( 'click', '.cmb2-remove-file-button', cmb.handleRemoveMedia ) // Repeatable content .on( 'click', '.cmb-add-group-row', cmb.addGroupRow ) .on( 'click', '.cmb-add-row-button', cmb.addAjaxRow ) .on( 'click', '.cmb-remove-group-row', cmb.removeGroupRow ) .on( 'click', '.cmb-remove-row-button', cmb.removeAjaxRow ) // Ajax oEmbed display .on( 'keyup paste focusout', '.cmb2-oembed', cmb.maybeOembed ) // Reset titles when removing a row .on( 'cmb2_remove_row', '.cmb-repeatable-group', cmb.resetTitlesAndIterator ) .on( 'click', '.cmbhandle, .cmbhandle + .cmbhandle-title', cmb.toggleHandle ); if ( $repeatGroup.length ) { $repeatGroup .on( 'cmb2_add_row', cmb.emptyValue ) .on( 'cmb2_add_row', cmb.setDefaults ) .filter('.sortable').each( function() { // Add sorting arrows $( this ).find( '.cmb-remove-group-row-button' ).before( '
    ' ); }) .on( 'click', '.cmb-shift-rows', cmb.shiftRows ); } // on pageload setTimeout( cmb.resizeoEmbeds, 500); // and on window resize $( window ).on( 'resize', cmb.resizeoEmbeds ); if ( $id( 'addtag' ).length ) { cmb.listenTagAdd(); } $( document ).on( 'cmb_init', cmb.mceEnsureSave ); cmb.trigger( 'cmb_init' ); }; // Handles updating tiny mce instances when saving a gutenberg post. // https://github.com/CMB2/CMB2/issues/1156 cmb.mceEnsureSave = function() { // If no wp.data, do not proceed (no gutenberg) if ( ! wp.data || ! wp.data.hasOwnProperty('subscribe') ) { return; } // If the current user cannot richedit, or MCE is not available, bail. if ( ! cmb.canTinyMCE() ) { return; } wp.data.subscribe( function() { var editor = wp.data.hasOwnProperty('select') ? wp.data.select( 'core/editor' ) : null; // the post is currently being saved && we have tinymce editors if ( editor && editor.isSavingPost && editor.isSavingPost() && window.tinyMCE.editors.length ) { for ( var i = 0; i < window.tinyMCE.editors.length; i++ ) { if ( window.tinyMCE.activeEditor !== window.tinyMCE.editors[i] ) { window.tinyMCE.editors[i].save(); } } } }); }; cmb.canTinyMCE = function() { return l10n.user_can_richedit && window.tinyMCE; }; cmb.listenTagAdd = function() { $document.ajaxSuccess( function( evt, xhr, settings ) { if ( settings.data && settings.data.length && -1 !== settings.data.indexOf( 'action=add-tag' ) ) { cmb.resetBoxes( $id( 'addtag' ).find( '.cmb2-wrap > .cmb2-metabox' ) ); } }); }; cmb.resetBoxes = function( $boxes ) { $.each( $boxes, function() { cmb.resetBox( $( this ) ); }); }; cmb.resetBox = function( $box ) { $box.find( '.wp-picker-clear' ).trigger( 'click' ); $box.find( '.cmb2-remove-file-button' ).trigger( 'click' ); $box.find( '.cmb-row.cmb-repeatable-grouping:not(:first-of-type) .cmb-remove-group-row' ).click(); $box.find( '.cmb-repeat-row:not(:first-child)' ).remove(); $box.find( 'input:not([type="button"]),select,textarea' ).each( function() { var $element = $( this ); var tagName = $element.prop('tagName'); if ( 'INPUT' === tagName ) { var elType = $element.attr( 'type' ); if ( 'checkbox' === elType || 'radio' === elType ) { $element.prop( 'checked', false ); } else { $element.val( '' ); } } if ( 'SELECT' === tagName ) { $( 'option:selected', this ).prop( 'selected', false ); } if ( 'TEXTAREA' === tagName ) { $element.html( '' ); } }); }; cmb.resetTitlesAndIterator = function( evt ) { if ( ! evt.group ) { return; } var $table = $( evt.target ); var groupTitle = $table.find( '.cmb-add-group-row' ).data( 'grouptitle' ); // Loop repeatable group table rows $table.find( '.cmb-repeatable-grouping' ).each( function( rowindex ) { var $row = $( this ); var prevIterator = parseInt( $row.data( 'iterator' ), 10 ); if ( prevIterator === rowindex ) { return; } // Reset rows iterator $row .attr( 'data-iterator', rowindex ) .data( 'iterator', rowindex ) .attr('id', getRowId( $row.attr('id'), rowindex ) ) .find( cmb.repeatEls ).each( function() { cmb.updateNameAttr( $( this ), prevIterator, rowindex ); }); cmb.resetGroupTitles( $row, rowindex, groupTitle ); }); }; cmb.resetGroupTitles = function( $row, newIterator, groupTitle ) { if ( groupTitle ) { var $rowTitle = $row.find( 'h3.cmb-group-title' ); // Reset rows title if ( $rowTitle.length ) { $rowTitle.text( groupTitle.replace( '{#}', parseInt( newIterator, 10 ) + 1 ) ); } } }; cmb.toggleHandle = function( evt ) { evt.preventDefault(); cmb.trigger( 'postbox-toggled', $( this ).parent('.postbox').toggleClass('closed') ); }; cmb.toggleCheckBoxes = function( evt ) { evt.preventDefault(); var $this = $( this ); var $multicheck = $this.closest( '.cmb-td' ).find( 'input[type=checkbox]:not([disabled])' ); var $toggled = ! $this.data( 'checked' ); $multicheck.prop( 'checked', $toggled ).trigger( 'change' ); $this.data( 'checked', $toggled ); }; cmb.handleMedia = function( evt ) { evt.preventDefault(); var $el = $( this ); cmb.attach_id = ! $el.hasClass( 'cmb2-upload-list' ) ? $el.closest( '.cmb-td' ).find( '.cmb2-upload-file-id' ).val() : false; // Clean up default 0 value cmb.attach_id = '0' !== cmb.attach_id ? cmb.attach_id : false; cmb.handleFieldMedia( $el.prev('input.cmb2-upload-file'), $el.hasClass( 'cmb2-upload-list' ) ); }; cmb.handleFileClick = function( evt ) { if ( $( evt.target ).is( 'a' ) ) { return; } evt.preventDefault(); var $el = $( this ); var $td = $el.closest( '.cmb-td' ); var isList = $td.find( '.cmb2-upload-button' ).hasClass( 'cmb2-upload-list' ); cmb.attach_id = isList ? $el.find( 'input[type="hidden"]' ).data( 'id' ) : $td.find( '.cmb2-upload-file-id' ).val(); if ( cmb.attach_id ) { cmb.handleFieldMedia( $td.find( 'input.cmb2-upload-file' ), isList ); } }; // Leaving this in for back-compat... cmb._handleMedia = function( id, isList ) { return cmb.handleFieldMedia( $id( id ), isList ); }; cmb.handleFieldMedia = function( $field, isList ) { if ( ! wp ) { return; } var id = $field.attr('id'); var fieldData = $field.data(); var media, handlers; // Get/set unique id since actual id cold _not_ be unique due to bad replacements, etc... var uid = fieldData.mediaid; if ( ! uid ) { uid = _.uniqueId(); $field.attr('data-mediaid', uid).data('mediaid', uid); fieldData.mediaid = uid; } handlers = cmb.mediaHandlers; media = cmb.media; media.mediaid = uid; media.field = id; media.$field = $field; media.fieldData = fieldData; media.previewSize = media.fieldData.previewsize; media.sizeName = media.fieldData.sizename; media.fieldName = $field.attr('name'); media.isList = isList; // If this field's media frame already exists, reopen it. if ( uid in media.frames ) { return media.frames[ uid ].open(); } // Create the media frame. media.frames[ uid ] = wp.media( { title: cmb.metabox().find('label[for="' + id + '"]').text(), library : media.fieldData.queryargs || {}, button: { text: l10n.strings[ isList ? 'upload_files' : 'upload_file' ] }, multiple: isList ? 'add' : false } ); // Enable the additional media filters: https://github.com/CMB2/CMB2/issues/873 media.frames[ uid ].states.first().set( 'filterable', 'all' ); cmb.trigger( 'cmb_media_modal_init', media ); handlers.list = function( selection, returnIt ) { // Setup our fileGroup array var fileGroup = []; var attachmentHtml; if ( ! handlers.list.templates ) { handlers.list.templates = { image : wp.template( 'cmb2-list-image' ), file : wp.template( 'cmb2-list-file' ), }; } // Loop through each attachment selection.each( function( attachment ) { // Image preview or standard generic output if it's not an image. attachmentHtml = handlers.getAttachmentHtml( attachment, 'list' ); // Add our file to our fileGroup array fileGroup.push( attachmentHtml ); }); if ( ! returnIt ) { // Append each item from our fileGroup array to .cmb2-media-status media.$field.siblings( '.cmb2-media-status' ).append( fileGroup ); } else { return fileGroup; } }; handlers.single = function( selection ) { if ( ! handlers.single.templates ) { handlers.single.templates = { image : wp.template( 'cmb2-single-image' ), file : wp.template( 'cmb2-single-file' ), }; } // Only get one file from the uploader var attachment = selection.first(); media.$field.val( attachment.get( 'url' ) ); media.$field.closest( '.cmb-td' ).find('.cmb2-upload-file-id') .val( attachment.get( 'id' ) ); // Image preview or standard generic output if it's not an image. var attachmentHtml = handlers.getAttachmentHtml( attachment, 'single' ); // add/display our output media.$field.siblings( '.cmb2-media-status' ).slideDown().html( attachmentHtml ); }; handlers.getAttachmentHtml = function( attachment, templatesId ) { var isImage = 'image' === attachment.get( 'type' ); var data = handlers.prepareData( attachment, isImage ); // Image preview or standard generic output if it's not an image. return handlers[ templatesId ].templates[ isImage ? 'image' : 'file' ]( data ); }; handlers.prepareData = function( data, image ) { if ( image ) { // Set the correct image size data handlers.getImageData.call( data, 50 ); } data = data.toJSON(); data.mediaField = media.field; data.mediaFieldName = media.fieldName; data.stringRemoveImage = l10n.strings.remove_image; data.stringFile = l10n.strings.file; data.stringDownload = l10n.strings.download; data.stringRemoveFile = l10n.strings.remove_file; return data; }; handlers.getImageData = function( fallbackSize ) { // Preview size dimensions var previewW = media.previewSize[0] || fallbackSize; var previewH = media.previewSize[1] || fallbackSize; // Image dimensions and url var url = this.get( 'url' ); var width = this.get( 'width' ); var height = this.get( 'height' ); var sizes = this.get( 'sizes' ); // Get the correct dimensions and url if a named size is set and exists // fallback to the 'large' size if ( sizes ) { if ( sizes[ media.sizeName ] ) { url = sizes[ media.sizeName ].url; width = sizes[ media.sizeName ].width; height = sizes[ media.sizeName ].height; } else if ( sizes.large ) { url = sizes.large.url; width = sizes.large.width; height = sizes.large.height; } } // Fit the image in to the preview size, keeping the correct aspect ratio if ( width > previewW ) { height = Math.floor( previewW * height / width ); width = previewW; } if ( height > previewH ) { width = Math.floor( previewH * width / height ); height = previewH; } if ( ! width ) { width = previewW; } if ( ! height ) { height = 'svg' === this.get( 'filename' ).split( '.' ).pop() ? '100%' : previewH; } this.set( 'sizeUrl', url ); this.set( 'sizeWidth', width ); this.set( 'sizeHeight', height ); return this; }; handlers.selectFile = function() { var selection = media.frames[ uid ].state().get( 'selection' ); var type = isList ? 'list' : 'single'; if ( cmb.attach_id && isList ) { $( '[data-id="'+ cmb.attach_id +'"]' ).parents( 'li' ).replaceWith( handlers.list( selection, true ) ); } else { handlers[type]( selection ); } cmb.trigger( 'cmb_media_modal_select', selection, media ); }; handlers.openModal = function() { var selection = media.frames[ uid ].state().get( 'selection' ); var attach; if ( ! cmb.attach_id ) { selection.reset(); } else { attach = wp.media.attachment( cmb.attach_id ); attach.fetch(); selection.set( attach ? [ attach ] : [] ); } cmb.trigger( 'cmb_media_modal_open', selection, media ); }; // When a file is selected, run a callback. media.frames[ uid ] .on( 'select', handlers.selectFile ) .on( 'open', handlers.openModal ); // Finally, open the modal media.frames[ uid ].open(); }; cmb.handleRemoveMedia = function( evt ) { evt.preventDefault(); var $this = $( this ); if ( $this.is( '.cmb-attach-list .cmb2-remove-file-button' ) ) { $this.parents( '.cmb2-media-item' ).remove(); return false; } var $cell = $this.closest( '.cmb-td' ); cmb.media.$field = $cell.find('.cmb2-upload-file'); cmb.media.field = cmb.media.$field.attr('id'); cmb.media.$field.val(''); $cell.find('.cmb2-upload-file-id').val(''); $this.parents('.cmb2-media-status').html(''); return false; }; cmb.cleanRow = function( $row, prevNum, group ) { var $elements = $row.find( cmb.repeatUpdate ); if ( group ) { var $other = $row.find( '[id]' ).not( cmb.repeatUpdate ); // Remove extra ajaxed rows $row.find('.cmb-repeat-table .cmb-repeat-row:not(:first-child)').remove(); // Update all elements w/ an ID if ( $other.length ) { $other.each( function() { var $_this = $( this ); var oldID = $_this.attr( 'id' ); var newID = oldID.replace( '_'+ prevNum, '_'+ cmb.idNumber ); var $buttons = $row.find('[data-selector="'+ oldID +'"]'); $_this.attr( 'id', newID ); // Replace data-selector vars if ( $buttons.length ) { $buttons.attr( 'data-selector', newID ).data( 'selector', newID ); } }); } } $elements.filter( ':checked' ).removeAttr( 'checked' ); $elements.find( ':checked' ).removeAttr( 'checked' ); $elements.filter( ':selected' ).removeAttr( 'selected' ); $elements.find( ':selected' ).removeAttr( 'selected', false ); cmb.resetGroupTitles( $row, cmb.idNumber, $row.data( 'title' ) ); $elements.each( function() { cmb.elReplacements( $( this ), prevNum, group ); } ); return cmb; }; cmb.elReplacements = function( $newInput, prevNum, group ) { var oldFor = $newInput.attr( 'for' ); var oldVal = $newInput.val(); var type = $newInput.prop( 'type' ); var defVal = cmb.getFieldArg( $newInput, 'default' ); var newVal = 'undefined' !== typeof defVal && false !== defVal ? defVal : ''; var tagName = $newInput.prop('tagName'); var checkable = 'radio' === type || 'checkbox' === type ? oldVal : false; var attrs = {}; var newID, oldID; if ( oldFor ) { attrs = { 'for' : oldFor.replace( '_'+ prevNum, '_'+ cmb.idNumber ) }; } else { var oldName = $newInput.attr( 'name' ); var newName; oldID = $newInput.attr( 'id' ); // Handle adding groups vs rows. if ( group ) { // Expect another bracket after group's index closing bracket. newName = oldName ? oldName.replace( '['+ prevNum +'][', '['+ cmb.idNumber +'][' ) : ''; // Expect another underscore after group's index trailing underscore. newID = oldID ? oldID.replace( '_' + prevNum + '_', '_' + cmb.idNumber + '_' ) : ''; } else { // Row indexes are at the very end of the string. newName = oldName ? cmb.replaceLast( oldName, '[' + prevNum + ']', '[' + cmb.idNumber + ']' ) : ''; newID = oldID ? cmb.replaceLast( oldID, '_' + prevNum, '_' + cmb.idNumber ) : ''; } attrs = { id: newID, name: newName }; } // Clear out textarea values if ( 'TEXTAREA' === tagName ) { $newInput.html( newVal ); } if ( 'SELECT' === tagName && 'undefined' !== typeof defVal ) { var $toSelect = $newInput.find( '[value="'+ defVal + '"]' ); if ( $toSelect.length ) { $toSelect.attr( 'selected', 'selected' ).prop( 'selected', 'selected' ); } } if ( checkable ) { $newInput.removeAttr( 'checked' ); if ( 'undefined' !== typeof defVal && oldVal === defVal ) { $newInput.attr( 'checked', 'checked' ).prop( 'checked', 'checked' ); } } if ( ! group && $newInput[0].hasAttribute( 'data-iterator' ) ) { attrs['data-iterator'] = cmb.idNumber; } $newInput .removeClass( 'hasDatepicker' ) .val( checkable ? checkable : newVal ).attr( attrs ); return $newInput; }; cmb.newRowHousekeeping = function( $row ) { var $colorPicker = $row.find( '.wp-picker-container' ); var $list = $row.find( '.cmb2-media-status' ); if ( $colorPicker.length ) { // Need to clean-up colorpicker before appending $colorPicker.each( function() { var $td = $( this ).parent(); $td.html( $td.find( 'input[type="text"].cmb2-colorpicker' ).attr('style', '') ); }); } // Need to clean-up colorpicker before appending if ( $list.length ) { $list.empty(); } return cmb; }; cmb.afterRowInsert = function( $row ) { // Init pickers from new row cmb.initPickers( $row.find('input[type="text"].cmb2-timepicker'), $row.find('input[type="text"].cmb2-datepicker'), $row.find('input[type="text"].cmb2-colorpicker') ); }; cmb.updateNameAttr = function ( $el, prevIterator, newIterator ) { var name = $el.attr( 'name' ); // get current name // If name is defined if ( 'undefined' !== typeof name ) { var isFileList = $el.attr( 'id' ).indexOf('filelist'); // Update field name attributes so data is not orphaned when a row is removed and post is saved var from = isFileList ? '[' + prevIterator + '][' : '[' + prevIterator + ']'; var to = isFileList ? '[' + newIterator + '][' : '[' + newIterator + ']'; var newName = name.replace( from, to ); // New name with replaced iterator $el.attr( 'name', newName ); } }; cmb.emptyValue = function( evt, row ) { $( cmb.noEmpty, row ).val( '' ); }; cmb.setDefaults = function( evt, row ) { $( cmb.noEmpty, row ).each( function() { var $el = $(this); var defVal = cmb.getFieldArg( $el, 'default' ); if ( 'undefined' !== typeof defVal && false !== defVal ) { $el.val( defVal ); } }); }; cmb.addGroupRow = function( evt ) { evt.preventDefault(); var $this = $( this ); // before anything significant happens cmb.triggerElement( $this, 'cmb2_add_group_row_start', $this ); var $table = $id( $this.data('selector') ); var $oldRow = $table.find('.cmb-repeatable-grouping').last(); var prevNum = parseInt( $oldRow.data('iterator'), 10 ); cmb.idNumber = parseInt( prevNum, 10 ) + 1; var $row = $oldRow.clone(); var nodeName = $row.prop('nodeName') || 'div'; // Make sure the next number doesn't exist. while ( $table.find( '.cmb-repeatable-grouping[data-iterator="'+ cmb.idNumber +'"]' ).length > 0 ) { cmb.idNumber++; } cmb.newRowHousekeeping( $row.data( 'title', $this.data( 'grouptitle' ) ) ).cleanRow( $row, prevNum, true ); $row.find( '.cmb-add-row-button' ).prop( 'disabled', false ); var $newRow = $( '<' + nodeName + ' id="'+ getRowId( $oldRow.attr('id'), cmb.idNumber ) +'" class="postbox cmb-row cmb-repeatable-grouping" data-iterator="'+ cmb.idNumber +'">'+ $row.html() +'' ); $oldRow.after( $newRow ); cmb.afterRowInsert( $newRow ); cmb.makeRepeatableSortable( $newRow ); cmb.triggerElement( $table, { type: 'cmb2_add_row', group: true }, $newRow ); }; cmb.addAjaxRow = function( evt ) { evt.preventDefault(); var $this = $( this ); var $table = $id( $this.data('selector') ); var $row = $table.find('.empty-row'); var prevNum = parseInt( $row.find('[data-iterator]').data('iterator'), 10 ); cmb.idNumber = parseInt( prevNum, 10 ) + 1; var $emptyrow = $row.clone(); cmb.newRowHousekeeping( $emptyrow ).cleanRow( $emptyrow, prevNum ); $row.removeClass('empty-row hidden').addClass('cmb-repeat-row'); $row.after( $emptyrow ); cmb.afterRowInsert( $emptyrow ); cmb.triggerElement( $table, { type: 'cmb2_add_row', group: false }, $emptyrow, $row ); }; cmb.removeGroupRow = function( evt ) { evt.preventDefault(); var $this = $( this ); var confirmation = $this.data('confirm'); // Process further only if deletion confirmation enabled and user agreed. if ( ! cmb.resetRow.resetting && confirmation && ! window.confirm( confirmation ) ) { return; } var $table = $id( $this.data('selector') ); var $parent = $this.parents('.cmb-repeatable-grouping'); var number = $table.find('.cmb-repeatable-grouping').length; if ( number < 2 ) { return cmb.resetRow( $parent.parents('.cmb-repeatable-group').find( '.cmb-add-group-row' ), $this ); } cmb.triggerElement( $table, 'cmb2_remove_group_row_start', $this ); $parent.remove(); cmb.triggerElement( $table, { type: 'cmb2_remove_row', group: true } ); }; cmb.removeAjaxRow = function( evt ) { evt.preventDefault(); var $this = $( this ); // Check if disabled if ( $this.hasClass( 'button-disabled' ) ) { return; } var $parent = $this.parents('.cmb-row'); var $table = $this.parents('.cmb-repeat-table'); var number = $table.find('.cmb-row').length; if ( number <= 2 ) { return cmb.resetRow( $parent.find( '.cmb-add-row-button' ), $this ); } if ( $parent.hasClass('empty-row') ) { $parent.prev().addClass( 'empty-row' ).removeClass('cmb-repeat-row'); } $this.parents('.cmb-repeat-table .cmb-row').remove(); cmb.triggerElement( $table, { type: 'cmb2_remove_row', group: false } ); }; cmb.resetRow = function( $addNewBtn, $removeBtn ) { cmb.resetRow.resetting = true; // Click the "add new" button followed by the "remove this" button // in order to reset the repeat row to empty values. $addNewBtn.trigger( 'click' ); $removeBtn.trigger( 'click' ); cmb.resetRow.resetting = false; }; cmb.shiftRows = function( evt ) { evt.preventDefault(); var $this = $( this ); var moveUp = $this.hasClass( 'move-up' ) ? true : false; var $from = $this.parents( '.cmb-repeatable-grouping' ); var $goto = $from[ moveUp ? 'prev' : 'next' ]( '.cmb-repeatable-grouping' ); // Before shift occurs. cmb.triggerElement( $this, 'cmb2_shift_rows_enter', $this, $from, $goto ); if ( ! $goto.length ) { return; } // About to shift cmb.triggerElement( $this, 'cmb2_shift_rows_start', $this, $from, $goto ); var fromIterator = $from.attr('data-iterator'); var toIterator = $goto.attr('data-iterator'); // Replace name attributes in both groups. $from.attr( 'data-iterator', toIterator ).find( cmb.repeatEls ).each( function() { cmb.updateNameAttr( $( this ), fromIterator, toIterator ); }); $goto.attr( 'data-iterator', fromIterator ).find( cmb.repeatEls ).each( function() { cmb.updateNameAttr( $( this ), toIterator, fromIterator ); }); // Replace titles in both groups. var groupTitle = $this.parents( '.cmb-repeatable-group' ).find('[data-grouptitle]').data( 'grouptitle' ); if ( groupTitle ) { cmb.resetGroupTitles( $from, toIterator, groupTitle ); cmb.resetGroupTitles( $goto, fromIterator, groupTitle ); } // Now move the group to it's destination. $goto[moveUp ? 'before' : 'after']( $from ); // Scroll to the top of the shifted group. $([document.documentElement, document.body]).animate({ scrollTop: $from.offset().top - 50 }, 300); // shift done cmb.triggerElement( $this, 'cmb2_shift_rows_complete', $this, $from, $goto ); }; cmb.initPickers = function( $timePickers, $datePickers, $colorPickers ) { cmb.trigger( 'cmb_init_pickers', { time: $timePickers, date: $datePickers, color: $colorPickers } ); // Initialize jQuery UI timepickers cmb.initDateTimePickers( $timePickers, 'timepicker', 'time_picker' ); // Initialize jQuery UI datepickers cmb.initDateTimePickers( $datePickers, 'datepicker', 'date_picker' ); // Initialize color picker cmb.initColorPickers( $colorPickers ); }; cmb.initDateTimePickers = function( $selector, method, defaultKey ) { if ( $selector.length ) { $selector[ method ]( 'destroy' ).each( function() { var $this = $( this ); var fieldOpts = $this.data( method ) || {}; var options = $.extend( {}, cmb.defaults[ defaultKey ], fieldOpts ); $this[ method ]( cmb.datePickerSetupOpts( fieldOpts, options, method ) ); } ); } }; cmb.datePickerSetupOpts = function( fieldOpts, options, method ) { var existing = $.extend( {}, options ); options.beforeShow = function( input, inst ) { if ( 'timepicker' === method ) { cmb.addTimePickerClasses( inst.dpDiv ); } // Wrap datepicker w/ class to narrow the scope of jQuery UI CSS and prevent conflicts $id( 'ui-datepicker-div' ).addClass( 'cmb2-element' ); // Let's be sure to call beforeShow if it was added if ( 'function' === typeof existing.beforeShow ) { existing.beforeShow( input, inst ); } }; if ( 'timepicker' === method ) { options.onChangeMonthYear = function( year, month, inst, picker ) { cmb.addTimePickerClasses( inst.dpDiv ); // Let's be sure to call onChangeMonthYear if it was added if ( 'function' === typeof existing.onChangeMonthYear ) { existing.onChangeMonthYear( year, month, inst, picker ); } }; } options.onClose = function( dateText, inst ) { // Remove the class when we're done with it (and hide to remove FOUC). var $picker = $id( 'ui-datepicker-div' ).removeClass( 'cmb2-element' ).hide(); if ( 'timepicker' === method && ! $( inst.input ).val() ) { // Set the timepicker field value if it's empty. inst.input.val( $picker.find( '.ui_tpicker_time' ).text() ); } // Let's be sure to call onClose if it was added if ( 'function' === typeof existing.onClose ) { existing.onClose( dateText, inst ); } }; return options; }; // Adds classes to timepicker buttons. cmb.addTimePickerClasses = function( $picker ) { var func = cmb.addTimePickerClasses; func.count = func.count || 0; // Wait a bit to let the timepicker render, since these are pre-render events. setTimeout( function() { if ( $picker.find( '.ui-priority-secondary' ).length ) { $picker.find( '.ui-priority-secondary' ).addClass( 'button-secondary' ); $picker.find( '.ui-priority-primary' ).addClass( 'button-primary' ); func.count = 0; } else if ( func.count < 5 ) { func.count++; func( $picker ); } }, 10 ); }; cmb.initColorPickers = function( $selector ) { if ( ! $selector.length ) { return; } if ( 'object' === typeof jQuery.wp && 'function' === typeof jQuery.wp.wpColorPicker ) { $selector.each( function() { var $this = $( this ); var fieldOpts = $this.data( 'colorpicker' ) || {}; $this.wpColorPicker( $.extend( {}, cmb.defaults.color_picker, fieldOpts ) ); } ); } else { $selector.each( function( i ) { $( this ).after( '
    ' ); $id( 'picker-' + i ).hide().farbtastic( $( this ) ); } ) .focus( function() { $( this ).next().show(); } ) .blur( function() { $( this ).next().hide(); } ); } }; cmb.initCodeEditors = function( $selector ) { cmb.trigger( 'cmb_init_code_editors', $selector ); if ( ! cmb.defaults.code_editor || ! wp || ! wp.codeEditor || ! $selector.length ) { return; } $selector.each( function() { wp.codeEditor.initialize( this.id, cmb.codeEditorArgs( $( this ).data( 'codeeditor' ) ) ); } ); }; cmb.codeEditorArgs = function( overrides ) { var props = [ 'codemirror', 'csslint', 'jshint', 'htmlhint' ]; var args = $.extend( {}, cmb.defaults.code_editor ); overrides = overrides || {}; for ( var i = props.length - 1; i >= 0; i-- ) { if ( overrides.hasOwnProperty( props[i] ) ) { args[ props[i] ] = $.extend( {}, args[ props[i] ] || {}, overrides[ props[i] ] ); } } return args; }; cmb.makeListSortable = function() { var $filelist = cmb.metabox().find( '.cmb2-media-status.cmb-attach-list' ); if ( $filelist.length ) { $filelist.sortable({ cursor: 'move' }).disableSelection(); } }; cmb.makeRepeatableSortable = function( $row ) { var $repeatables = ($row || cmb.metabox()).find( '.cmb-repeat-table .cmb-field-list' ); if ( $repeatables.length ) { $repeatables.sortable({ items : '.cmb-repeat-row', cursor: 'move', // The default "cancel" attributes are: "input,textarea,button,select,option". // We are appending .CodeMirror. // See https://api.jqueryui.com/sortable/#option-cancel cancel: 'input,textarea,button,select,option,.CodeMirror' }); } }; cmb.maybeOembed = function( evt ) { var $this = $( this ); var m = { focusout : function() { setTimeout( function() { // if it's been 2 seconds, hide our spinner cmb.spinner( '.cmb2-metabox', true ); }, 2000); }, keyup : function() { var betw = function( min, max ) { return ( evt.which <= max && evt.which >= min ); }; // Only Ajax on normal keystrokes if ( betw( 48, 90 ) || betw( 96, 111 ) || betw( 8, 9 ) || evt.which === 187 || evt.which === 190 ) { // fire our ajax function cmb.doAjax( $this, evt ); } }, paste : function() { // paste event is fired before the value is filled, so wait a bit setTimeout( function() { cmb.doAjax( $this ); }, 100); } }; m[ evt.type ](); }; /** * Resize oEmbed videos to fit in their respective metaboxes * * @since 0.9.4 * * @return {return} */ cmb.resizeoEmbeds = function() { cmb.metabox().each( function() { var $this = $( this ); var $tableWrap = $this.parents('.inside'); var isSide = $this.parents('.inner-sidebar').length || $this.parents( '#side-sortables' ).length; var isSmall = isSide; var isSmallest = false; if ( ! $tableWrap.length ) { return true; // continue } // Calculate new width var tableW = $tableWrap.width(); if ( cmb.styleBreakPoint > tableW ) { isSmall = true; isSmallest = ( cmb.styleBreakPoint - 62 ) > tableW; } tableW = isSmall ? tableW : Math.round(($tableWrap.width() * 0.82)*0.97); var newWidth = tableW - 30; if ( isSmall && ! isSide && ! isSmallest ) { newWidth = newWidth - 75; } if ( newWidth > 639 ) { return true; // continue } var $embeds = $this.find('.cmb-type-oembed .embed-status'); var $children = $embeds.children().not('.cmb2-remove-wrapper'); if ( ! $children.length ) { return true; // continue } $children.each( function() { var $this = $( this ); var iwidth = $this.width(); var iheight = $this.height(); var _newWidth = newWidth; if ( $this.parents( '.cmb-repeat-row' ).length && ! isSmall ) { // Make room for our repeatable "remove" button column _newWidth = newWidth - 91; _newWidth = 785 > tableW ? _newWidth - 15 : _newWidth; } // Calc new height var newHeight = Math.round((_newWidth * iheight)/iwidth); $this.width(_newWidth).height(newHeight); }); }); }; // function for running our ajax cmb.doAjax = function( $obj ) { // get typed value var oembed_url = $obj.val(); // only proceed if the field contains more than 6 characters if ( oembed_url.length < 6 ) { return; } // get field id var field_id = $obj.attr('id'); var $context = $obj.closest( '.cmb-td' ); var $embed_container = $context.find( '.embed-status' ); var $embed_wrap = $context.find( '.embed_wrap' ); var $child_el = $embed_container.find( ':first-child' ); var oembed_width = $embed_container.length && $child_el.length ? $child_el.width() : $obj.width(); cmb.log( 'oembed_url', oembed_url, field_id ); // show our spinner cmb.spinner( $context ); // clear out previous results $embed_wrap.html(''); // and run our ajax function setTimeout( function() { // if they haven't typed in 500 ms if ( $( '.cmb2-oembed:focus' ).val() !== oembed_url ) { return; } $.ajax({ type : 'post', dataType : 'json', url : l10n.ajaxurl, data : { 'action' : 'cmb2_oembed_handler', 'oembed_url' : oembed_url, 'oembed_width' : oembed_width > 300 ? oembed_width : 300, 'field_id' : field_id, 'object_id' : $obj.data( 'objectid' ), 'object_type' : $obj.data( 'objecttype' ), 'cmb2_ajax_nonce' : l10n.ajax_nonce }, success: function(response) { cmb.log( response ); // hide our spinner cmb.spinner( $context, true ); // and populate our results from ajax response $embed_wrap.html( response.data ); } }); }, 500); }; /** * Gets jQuery object containing all CMB metaboxes. Caches the result. * * @since 1.0.2 * * @return {Object} jQuery object containing all CMB metaboxes. */ cmb.metabox = function() { if ( cmb.$metabox ) { return cmb.$metabox; } cmb.$metabox = $('.cmb2-wrap > .cmb2-metabox'); return cmb.$metabox; }; /** * Starts/stops contextual spinner. * * @since 1.0.1 * * @param {object} $context The jQuery parent/context object. * @param {bool} hide Whether to hide the spinner (will show by default). * * @return {void} */ cmb.spinner = function( $context, hide ) { var m = hide ? 'removeClass' : 'addClass'; $('.cmb-spinner', $context )[ m ]( 'is-active' ); }; /** * Triggers a jQuery event on the document object. * * @since 2.2.3 * * @param {string} evtName The name of the event to trigger. * * @return {void} */ cmb.trigger = function( evtName ) { var args = Array.prototype.slice.call( arguments, 1 ); args.push( cmb ); $document.trigger( evtName, args ); }; /** * Triggers a jQuery event on the given jQuery object. * * @since 2.2.3 * * @param {object} $el The jQuery element object. * @param {string} evtName The name of the event to trigger. * * @return {void} */ cmb.triggerElement = function( $el, evtName ) { var args = Array.prototype.slice.call( arguments, 2 ); args.push( cmb ); $el.trigger( evtName, args ); }; /** * Get an argument for a given field. * * @since 2.5.0 * * @param {string|object} hash The field hash, id, or a jQuery object for a field. * @param {string} arg The argument to get on the field. * * @return {mixed} The argument value. */ cmb.getFieldArg = function( hash, arg ) { return cmb.getField( hash )[ arg ]; }; /** * Get a field object instances. Can be filtered by passing in a filter callback function. * e.g. `const fileFields = CMB2.getFields(f => 'file' === f.type);` * * @since 2.5.0 * * @param {mixed} filterCb An optional filter callback function. * * @return array An array of field object instances. */ cmb.getFields = function( filterCb ) { if ( 'function' === typeof filterCb ) { var fields = []; $.each( l10n.fields, function( hash, field ) { if ( filterCb( field, hash ) ) { fields.push( field ); } }); return fields; } return l10n.fields; }; /** * Get a field object instance by hash or id. * * @since 2.5.0 * * @param {string|object} hash The field hash, id, or a jQuery object for a field. * * @return {object} The field object or an empty object. */ cmb.getField = function( hash ) { var field = {}; hash = hash instanceof jQuery ? hash.data( 'hash' ) : hash; if ( hash ) { try { if ( l10n.fields[ hash ] ) { throw new Error( hash ); } cmb.getFields( function( field ) { if ( 'function' === typeof hash ) { if ( hash( field ) ) { throw new Error( field.hash ); } } else if ( field.id && field.id === hash ) { throw new Error( field.hash ); } }); } catch( e ) { field = l10n.fields[ e.message ]; } } return field; }; /** * Safely log things if query var is set. Accepts same parameters as console.log. * * @since 1.0.0 * * @return {void} */ cmb.log = function() { if ( l10n.script_debug && console && 'function' === typeof console.log ) { console.log.apply(console, arguments); } }; /** * Replace the last occurrence of a string. * * @since 2.2.6 * * @param {string} string String to search/replace. * @param {string} search String to search. * @param {string} replace String to replace search with. * * @return {string} Possibly modified string. */ cmb.replaceLast = function( string, search, replace ) { // find the index of last time word was used var n = string.lastIndexOf( search ); // slice the string in 2, one from the start to the lastIndexOf // and then replace the word in the rest return string.slice( 0, n ) + string.slice( n ).replace( search, replace ); }; // Kick it off! $( cmb.init ); })(window, document, jQuery, window.CMB2); cmb2.min.js000064400000076216151717007610006530 0ustar00window.CMB2=window.CMB2||{},function(window,document,$,cmb,undefined){"use strict";var $document,l10n=window.cmb2_l10,setTimeout=window.setTimeout,$id=function(selector){return $(document.getElementById(selector))},getRowId=function(id,newIterator){return id=id.split("-"),id.splice(id.length-1,1),id.push(newIterator),id.join("-")};cmb.$id=$id;var defaults={idNumber:!1,repeatEls:'input:not([type="button"]),select,textarea,.cmb2-media-status',noEmpty:'input:not([type="button"]):not([type="radio"]):not([type="checkbox"]),textarea',repeatUpdate:'input:not([type="button"]),select,textarea,label',styleBreakPoint:450,mediaHandlers:{},defaults:{time_picker:l10n.defaults.time_picker,date_picker:l10n.defaults.date_picker,color_picker:l10n.defaults.color_picker||{},code_editor:l10n.defaults.code_editor},media:{frames:{}}};cmb.init=function(){$document=$(document),$.extend(cmb,defaults),cmb.trigger("cmb_pre_init");var $metabox=cmb.metabox(),$repeatGroup=$metabox.find(".cmb-repeatable-group");cmb.initPickers($metabox.find('input[type="text"].cmb2-timepicker'),$metabox.find('input[type="text"].cmb2-datepicker'),$metabox.find('input[type="text"].cmb2-colorpicker')),cmb.initCodeEditors($metabox.find(".cmb2-textarea-code:not(.disable-codemirror)")),$('

    '+l10n.strings.check_toggle+"

    ").insertBefore(".cmb2-checkbox-list:not(.no-select-all)"),cmb.makeListSortable(),cmb.makeRepeatableSortable(),$metabox.on("change",".cmb2_upload_file",function(){cmb.media.field=$(this).attr("id"),$id(cmb.media.field+"_id").val("")}).on("click",".cmb-multicheck-toggle",cmb.toggleCheckBoxes).on("click",".cmb2-upload-button",cmb.handleMedia).on("click",".cmb-attach-list li, .cmb2-media-status .img-status img, .cmb2-media-status .file-status > span",cmb.handleFileClick).on("click",".cmb2-remove-file-button",cmb.handleRemoveMedia).on("click",".cmb-add-group-row",cmb.addGroupRow).on("click",".cmb-add-row-button",cmb.addAjaxRow).on("click",".cmb-remove-group-row",cmb.removeGroupRow).on("click",".cmb-remove-row-button",cmb.removeAjaxRow).on("keyup paste focusout",".cmb2-oembed",cmb.maybeOembed).on("cmb2_remove_row",".cmb-repeatable-group",cmb.resetTitlesAndIterator).on("click",".cmbhandle, .cmbhandle + .cmbhandle-title",cmb.toggleHandle),$repeatGroup.length&&$repeatGroup.on("cmb2_add_row",cmb.emptyValue).on("cmb2_add_row",cmb.setDefaults).filter(".sortable").each(function(){$(this).find(".cmb-remove-group-row-button").before(' ')}).on("click",".cmb-shift-rows",cmb.shiftRows),setTimeout(cmb.resizeoEmbeds,500),$(window).on("resize",cmb.resizeoEmbeds),$id("addtag").length&&cmb.listenTagAdd(),$(document).on("cmb_init",cmb.mceEnsureSave),cmb.trigger("cmb_init")},cmb.mceEnsureSave=function(){wp.data&&wp.data.hasOwnProperty("subscribe")&&cmb.canTinyMCE()&&wp.data.subscribe(function(){var editor=wp.data.hasOwnProperty("select")?wp.data.select("core/editor"):null;if(editor&&editor.isSavingPost&&editor.isSavingPost()&&window.tinyMCE.editors.length)for(var i=0;i .cmb2-metabox"))})},cmb.resetBoxes=function($boxes){$.each($boxes,function(){cmb.resetBox($(this))})},cmb.resetBox=function($box){$box.find(".wp-picker-clear").trigger("click"),$box.find(".cmb2-remove-file-button").trigger("click"),$box.find(".cmb-row.cmb-repeatable-grouping:not(:first-of-type) .cmb-remove-group-row").click(),$box.find(".cmb-repeat-row:not(:first-child)").remove(),$box.find('input:not([type="button"]),select,textarea').each(function(){var $element=$(this),tagName=$element.prop("tagName");if("INPUT"===tagName){var elType=$element.attr("type");"checkbox"===elType||"radio"===elType?$element.prop("checked",!1):$element.val("")}"SELECT"===tagName&&$("option:selected",this).prop("selected",!1),"TEXTAREA"===tagName&&$element.html("")})},cmb.resetTitlesAndIterator=function(evt){if(evt.group){var $table=$(evt.target),groupTitle=$table.find(".cmb-add-group-row").data("grouptitle");$table.find(".cmb-repeatable-grouping").each(function(rowindex){var $row=$(this),prevIterator=parseInt($row.data("iterator"),10);prevIterator!==rowindex&&($row.attr("data-iterator",rowindex).data("iterator",rowindex).attr("id",getRowId($row.attr("id"),rowindex)).find(cmb.repeatEls).each(function(){cmb.updateNameAttr($(this),prevIterator,rowindex)}),cmb.resetGroupTitles($row,rowindex,groupTitle))})}},cmb.resetGroupTitles=function($row,newIterator,groupTitle){if(groupTitle){var $rowTitle=$row.find("h3.cmb-group-title");$rowTitle.length&&$rowTitle.text(groupTitle.replace("{#}",parseInt(newIterator,10)+1))}},cmb.toggleHandle=function(evt){evt.preventDefault(),cmb.trigger("postbox-toggled",$(this).parent(".postbox").toggleClass("closed"))},cmb.toggleCheckBoxes=function(evt){evt.preventDefault();var $this=$(this),$multicheck=$this.closest(".cmb-td").find("input[type=checkbox]:not([disabled])"),$toggled=!$this.data("checked");$multicheck.prop("checked",$toggled).trigger("change"),$this.data("checked",$toggled)},cmb.handleMedia=function(evt){evt.preventDefault();var $el=$(this);cmb.attach_id=!$el.hasClass("cmb2-upload-list")&&$el.closest(".cmb-td").find(".cmb2-upload-file-id").val(),cmb.attach_id="0"!==cmb.attach_id&&cmb.attach_id,cmb.handleFieldMedia($el.prev("input.cmb2-upload-file"),$el.hasClass("cmb2-upload-list"))},cmb.handleFileClick=function(evt){if(!$(evt.target).is("a")){evt.preventDefault();var $el=$(this),$td=$el.closest(".cmb-td"),isList=$td.find(".cmb2-upload-button").hasClass("cmb2-upload-list");cmb.attach_id=isList?$el.find('input[type="hidden"]').data("id"):$td.find(".cmb2-upload-file-id").val(),cmb.attach_id&&cmb.handleFieldMedia($td.find("input.cmb2-upload-file"),isList)}},cmb._handleMedia=function(id,isList){return cmb.handleFieldMedia($id(id),isList)},cmb.handleFieldMedia=function($field,isList){if(wp){var media,handlers,id=$field.attr("id"),fieldData=$field.data(),uid=fieldData.mediaid;if(uid||(uid=_.uniqueId(),$field.attr("data-mediaid",uid).data("mediaid",uid),fieldData.mediaid=uid),handlers=cmb.mediaHandlers,media=cmb.media,media.mediaid=uid,media.field=id,media.$field=$field,media.fieldData=fieldData,media.previewSize=media.fieldData.previewsize,media.sizeName=media.fieldData.sizename,media.fieldName=$field.attr("name"),media.isList=isList,uid in media.frames)return media.frames[uid].open();media.frames[uid]=wp.media({title:cmb.metabox().find('label[for="'+id+'"]').text(),library:media.fieldData.queryargs||{},button:{text:l10n.strings[isList?"upload_files":"upload_file"]},multiple:!!isList&&"add"}),media.frames[uid].states.first().set("filterable","all"),cmb.trigger("cmb_media_modal_init",media),handlers.list=function(selection,returnIt){var attachmentHtml,fileGroup=[];if(handlers.list.templates||(handlers.list.templates={image:wp.template("cmb2-list-image"),file:wp.template("cmb2-list-file")}),selection.each(function(attachment){attachmentHtml=handlers.getAttachmentHtml(attachment,"list"),fileGroup.push(attachmentHtml)}),returnIt)return fileGroup;media.$field.siblings(".cmb2-media-status").append(fileGroup)},handlers.single=function(selection){handlers.single.templates||(handlers.single.templates={image:wp.template("cmb2-single-image"),file:wp.template("cmb2-single-file")});var attachment=selection.first();media.$field.val(attachment.get("url")),media.$field.closest(".cmb-td").find(".cmb2-upload-file-id").val(attachment.get("id"));var attachmentHtml=handlers.getAttachmentHtml(attachment,"single");media.$field.siblings(".cmb2-media-status").slideDown().html(attachmentHtml)},handlers.getAttachmentHtml=function(attachment,templatesId){var isImage="image"===attachment.get("type"),data=handlers.prepareData(attachment,isImage);return handlers[templatesId].templates[isImage?"image":"file"](data)},handlers.prepareData=function(data,image){return image&&handlers.getImageData.call(data,50),data=data.toJSON(),data.mediaField=media.field,data.mediaFieldName=media.fieldName,data.stringRemoveImage=l10n.strings.remove_image,data.stringFile=l10n.strings.file,data.stringDownload=l10n.strings.download,data.stringRemoveFile=l10n.strings.remove_file,data},handlers.getImageData=function(fallbackSize){var previewW=media.previewSize[0]||fallbackSize,previewH=media.previewSize[1]||fallbackSize,url=this.get("url"),width=this.get("width"),height=this.get("height"),sizes=this.get("sizes");return sizes&&(sizes[media.sizeName]?(url=sizes[media.sizeName].url,width=sizes[media.sizeName].width,height=sizes[media.sizeName].height):sizes.large&&(url=sizes.large.url,width=sizes.large.width,height=sizes.large.height)),width>previewW&&(height=Math.floor(previewW*height/width),width=previewW),height>previewH&&(width=Math.floor(previewH*width/height),height=previewH),width||(width=previewW),height||(height="svg"===this.get("filename").split(".").pop()?"100%":previewH),this.set("sizeUrl",url),this.set("sizeWidth",width),this.set("sizeHeight",height),this},handlers.selectFile=function(){var selection=media.frames[uid].state().get("selection"),type=isList?"list":"single";cmb.attach_id&&isList?$('[data-id="'+cmb.attach_id+'"]').parents("li").replaceWith(handlers.list(selection,!0)):handlers[type](selection),cmb.trigger("cmb_media_modal_select",selection,media)},handlers.openModal=function(){var attach,selection=media.frames[uid].state().get("selection");cmb.attach_id?(attach=wp.media.attachment(cmb.attach_id),attach.fetch(),selection.set(attach?[attach]:[])):selection.reset(),cmb.trigger("cmb_media_modal_open",selection,media)},media.frames[uid].on("select",handlers.selectFile).on("open",handlers.openModal),media.frames[uid].open()}},cmb.handleRemoveMedia=function(evt){evt.preventDefault();var $this=$(this);if($this.is(".cmb-attach-list .cmb2-remove-file-button"))return $this.parents(".cmb2-media-item").remove(),!1;var $cell=$this.closest(".cmb-td");return cmb.media.$field=$cell.find(".cmb2-upload-file"),cmb.media.field=cmb.media.$field.attr("id"),cmb.media.$field.val(""),$cell.find(".cmb2-upload-file-id").val(""),$this.parents(".cmb2-media-status").html(""),!1},cmb.cleanRow=function($row,prevNum,group){var $elements=$row.find(cmb.repeatUpdate);if(group){var $other=$row.find("[id]").not(cmb.repeatUpdate);$row.find(".cmb-repeat-table .cmb-repeat-row:not(:first-child)").remove(),$other.length&&$other.each(function(){var $_this=$(this),oldID=$_this.attr("id"),newID=oldID.replace("_"+prevNum,"_"+cmb.idNumber),$buttons=$row.find('[data-selector="'+oldID+'"]');$_this.attr("id",newID),$buttons.length&&$buttons.attr("data-selector",newID).data("selector",newID)})}return $elements.filter(":checked").removeAttr("checked"),$elements.find(":checked").removeAttr("checked"),$elements.filter(":selected").removeAttr("selected"),$elements.find(":selected").removeAttr("selected",!1),cmb.resetGroupTitles($row,cmb.idNumber,$row.data("title")),$elements.each(function(){cmb.elReplacements($(this),prevNum,group)}),cmb},cmb.elReplacements=function($newInput,prevNum,group){var newID,oldID,oldFor=$newInput.attr("for"),oldVal=$newInput.val(),type=$newInput.prop("type"),defVal=cmb.getFieldArg($newInput,"default"),newVal=void 0!==defVal&&!1!==defVal?defVal:"",tagName=$newInput.prop("tagName"),checkable=("radio"===type||"checkbox"===type)&&oldVal,attrs={};if(oldFor)attrs={for:oldFor.replace("_"+prevNum,"_"+cmb.idNumber)};else{var newName,oldName=$newInput.attr("name");oldID=$newInput.attr("id"),group?(newName=oldName?oldName.replace("["+prevNum+"][","["+cmb.idNumber+"]["):"",newID=oldID?oldID.replace("_"+prevNum+"_","_"+cmb.idNumber+"_"):""):(newName=oldName?cmb.replaceLast(oldName,"["+prevNum+"]","["+cmb.idNumber+"]"):"",newID=oldID?cmb.replaceLast(oldID,"_"+prevNum,"_"+cmb.idNumber):""),attrs={id:newID,name:newName}}if("TEXTAREA"===tagName&&$newInput.html(newVal),"SELECT"===tagName&&void 0!==defVal){var $toSelect=$newInput.find('[value="'+defVal+'"]');$toSelect.length&&$toSelect.attr("selected","selected").prop("selected","selected")}return checkable&&($newInput.removeAttr("checked"),void 0!==defVal&&oldVal===defVal&&$newInput.attr("checked","checked").prop("checked","checked")),!group&&$newInput[0].hasAttribute("data-iterator")&&(attrs["data-iterator"]=cmb.idNumber),$newInput.removeClass("hasDatepicker").val(checkable||newVal).attr(attrs),$newInput},cmb.newRowHousekeeping=function($row){var $colorPicker=$row.find(".wp-picker-container"),$list=$row.find(".cmb2-media-status");return $colorPicker.length&&$colorPicker.each(function(){var $td=$(this).parent();$td.html($td.find('input[type="text"].cmb2-colorpicker').attr("style",""))}),$list.length&&$list.empty(),cmb},cmb.afterRowInsert=function($row){cmb.initPickers($row.find('input[type="text"].cmb2-timepicker'),$row.find('input[type="text"].cmb2-datepicker'),$row.find('input[type="text"].cmb2-colorpicker'))},cmb.updateNameAttr=function($el,prevIterator,newIterator){var name=$el.attr("name");if(void 0!==name){var isFileList=$el.attr("id").indexOf("filelist"),from=isFileList?"["+prevIterator+"][":"["+prevIterator+"]",to=isFileList?"["+newIterator+"][":"["+newIterator+"]",newName=name.replace(from,to);$el.attr("name",newName)}},cmb.emptyValue=function(evt,row){$(cmb.noEmpty,row).val("")},cmb.setDefaults=function(evt,row){$(cmb.noEmpty,row).each(function(){var $el=$(this),defVal=cmb.getFieldArg($el,"default");void 0!==defVal&&!1!==defVal&&$el.val(defVal)})},cmb.addGroupRow=function(evt){evt.preventDefault();var $this=$(this);cmb.triggerElement($this,"cmb2_add_group_row_start",$this);var $table=$id($this.data("selector")),$oldRow=$table.find(".cmb-repeatable-grouping").last(),prevNum=parseInt($oldRow.data("iterator"),10);cmb.idNumber=parseInt(prevNum,10)+1;for(var $row=$oldRow.clone(),nodeName=$row.prop("nodeName")||"div";$table.find('.cmb-repeatable-grouping[data-iterator="'+cmb.idNumber+'"]').length>0;)cmb.idNumber++;cmb.newRowHousekeeping($row.data("title",$this.data("grouptitle"))).cleanRow($row,prevNum,!0),$row.find(".cmb-add-row-button").prop("disabled",!1);var $newRow=$("<"+nodeName+' id="'+getRowId($oldRow.attr("id"),cmb.idNumber)+'" class="postbox cmb-row cmb-repeatable-grouping" data-iterator="'+cmb.idNumber+'">'+$row.html()+"");$oldRow.after($newRow),cmb.afterRowInsert($newRow),cmb.makeRepeatableSortable($newRow),cmb.triggerElement($table,{type:"cmb2_add_row",group:!0},$newRow)},cmb.addAjaxRow=function(evt){evt.preventDefault();var $this=$(this),$table=$id($this.data("selector")),$row=$table.find(".empty-row"),prevNum=parseInt($row.find("[data-iterator]").data("iterator"),10);cmb.idNumber=parseInt(prevNum,10)+1;var $emptyrow=$row.clone();cmb.newRowHousekeeping($emptyrow).cleanRow($emptyrow,prevNum),$row.removeClass("empty-row hidden").addClass("cmb-repeat-row"),$row.after($emptyrow),cmb.afterRowInsert($emptyrow),cmb.triggerElement($table,{type:"cmb2_add_row",group:!1},$emptyrow,$row)},cmb.removeGroupRow=function(evt){evt.preventDefault();var $this=$(this),confirmation=$this.data("confirm");if(cmb.resetRow.resetting||!confirmation||window.confirm(confirmation)){var $table=$id($this.data("selector")),$parent=$this.parents(".cmb-repeatable-grouping");if($table.find(".cmb-repeatable-grouping").length<2)return cmb.resetRow($parent.parents(".cmb-repeatable-group").find(".cmb-add-group-row"),$this);cmb.triggerElement($table,"cmb2_remove_group_row_start",$this),$parent.remove(),cmb.triggerElement($table,{type:"cmb2_remove_row",group:!0})}},cmb.removeAjaxRow=function(evt){evt.preventDefault();var $this=$(this);if(!$this.hasClass("button-disabled")){var $parent=$this.parents(".cmb-row"),$table=$this.parents(".cmb-repeat-table");if($table.find(".cmb-row").length<=2)return cmb.resetRow($parent.find(".cmb-add-row-button"),$this);$parent.hasClass("empty-row")&&$parent.prev().addClass("empty-row").removeClass("cmb-repeat-row"),$this.parents(".cmb-repeat-table .cmb-row").remove(),cmb.triggerElement($table,{type:"cmb2_remove_row",group:!1})}},cmb.resetRow=function($addNewBtn,$removeBtn){cmb.resetRow.resetting=!0,$addNewBtn.trigger("click"),$removeBtn.trigger("click"),cmb.resetRow.resetting=!1},cmb.shiftRows=function(evt){evt.preventDefault();var $this=$(this),moveUp=!!$this.hasClass("move-up"),$from=$this.parents(".cmb-repeatable-grouping"),$goto=$from[moveUp?"prev":"next"](".cmb-repeatable-grouping");if(cmb.triggerElement($this,"cmb2_shift_rows_enter",$this,$from,$goto),$goto.length){cmb.triggerElement($this,"cmb2_shift_rows_start",$this,$from,$goto);var fromIterator=$from.attr("data-iterator"),toIterator=$goto.attr("data-iterator");$from.attr("data-iterator",toIterator).find(cmb.repeatEls).each(function(){cmb.updateNameAttr($(this),fromIterator,toIterator)}),$goto.attr("data-iterator",fromIterator).find(cmb.repeatEls).each(function(){cmb.updateNameAttr($(this),toIterator,fromIterator)});var groupTitle=$this.parents(".cmb-repeatable-group").find("[data-grouptitle]").data("grouptitle");groupTitle&&(cmb.resetGroupTitles($from,toIterator,groupTitle),cmb.resetGroupTitles($goto,fromIterator,groupTitle)),$goto[moveUp?"before":"after"]($from),$([document.documentElement,document.body]).animate({scrollTop:$from.offset().top-50},300),cmb.triggerElement($this,"cmb2_shift_rows_complete",$this,$from,$goto)}},cmb.initPickers=function($timePickers,$datePickers,$colorPickers){cmb.trigger("cmb_init_pickers",{time:$timePickers,date:$datePickers,color:$colorPickers}),cmb.initDateTimePickers($timePickers,"timepicker","time_picker"),cmb.initDateTimePickers($datePickers,"datepicker","date_picker"),cmb.initColorPickers($colorPickers)},cmb.initDateTimePickers=function($selector,method,defaultKey){$selector.length&&$selector[method]("destroy").each(function(){var $this=$(this),fieldOpts=$this.data(method)||{},options=$.extend({},cmb.defaults[defaultKey],fieldOpts);$this[method](cmb.datePickerSetupOpts(fieldOpts,options,method))})},cmb.datePickerSetupOpts=function(fieldOpts,options,method){var existing=$.extend({},options);return options.beforeShow=function(input,inst){"timepicker"===method&&cmb.addTimePickerClasses(inst.dpDiv),$id("ui-datepicker-div").addClass("cmb2-element"),"function"==typeof existing.beforeShow&&existing.beforeShow(input,inst)},"timepicker"===method&&(options.onChangeMonthYear=function(year,month,inst,picker){cmb.addTimePickerClasses(inst.dpDiv),"function"==typeof existing.onChangeMonthYear&&existing.onChangeMonthYear(year,month,inst,picker)}),options.onClose=function(dateText,inst){var $picker=$id("ui-datepicker-div").removeClass("cmb2-element").hide();"timepicker"!==method||$(inst.input).val()||inst.input.val($picker.find(".ui_tpicker_time").text()),"function"==typeof existing.onClose&&existing.onClose(dateText,inst)},options},cmb.addTimePickerClasses=function($picker){var func=cmb.addTimePickerClasses;func.count=func.count||0,setTimeout(function(){$picker.find(".ui-priority-secondary").length?($picker.find(".ui-priority-secondary").addClass("button-secondary"),$picker.find(".ui-priority-primary").addClass("button-primary"),func.count=0):func.count<5&&(func.count++,func($picker))},10)},cmb.initColorPickers=function($selector){$selector.length&&("object"==typeof jQuery.wp&&"function"==typeof jQuery.wp.wpColorPicker?$selector.each(function(){var $this=$(this),fieldOpts=$this.data("colorpicker")||{};$this.wpColorPicker($.extend({},cmb.defaults.color_picker,fieldOpts))}):$selector.each(function(i){$(this).after('
    '),$id("picker-"+i).hide().farbtastic($(this))}).focus(function(){$(this).next().show()}).blur(function(){$(this).next().hide()}))},cmb.initCodeEditors=function($selector){cmb.trigger("cmb_init_code_editors",$selector),cmb.defaults.code_editor&&wp&&wp.codeEditor&&$selector.length&&$selector.each(function(){wp.codeEditor.initialize(this.id,cmb.codeEditorArgs($(this).data("codeeditor")))})},cmb.codeEditorArgs=function(overrides){var props=["codemirror","csslint","jshint","htmlhint"],args=$.extend({},cmb.defaults.code_editor);overrides=overrides||{};for(var i=props.length-1;i>=0;i--)overrides.hasOwnProperty(props[i])&&(args[props[i]]=$.extend({},args[props[i]]||{},overrides[props[i]]));return args},cmb.makeListSortable=function(){var $filelist=cmb.metabox().find(".cmb2-media-status.cmb-attach-list");$filelist.length&&$filelist.sortable({cursor:"move"}).disableSelection()},cmb.makeRepeatableSortable=function($row){var $repeatables=($row||cmb.metabox()).find(".cmb-repeat-table .cmb-field-list");$repeatables.length&&$repeatables.sortable({items:".cmb-repeat-row",cursor:"move",cancel:"input,textarea,button,select,option,.CodeMirror"})},cmb.maybeOembed=function(evt){var $this=$(this);({focusout:function(){setTimeout(function(){cmb.spinner(".cmb2-metabox",!0)},2e3)},keyup:function(){var betw=function(min,max){return evt.which<=max&&evt.which>=min};(betw(48,90)||betw(96,111)||betw(8,9)||187===evt.which||190===evt.which)&&cmb.doAjax($this,evt)},paste:function(){setTimeout(function(){cmb.doAjax($this)},100)}})[evt.type]()},cmb.resizeoEmbeds=function(){cmb.metabox().each(function(){var $this=$(this),$tableWrap=$this.parents(".inside"),isSide=$this.parents(".inner-sidebar").length||$this.parents("#side-sortables").length,isSmall=isSide,isSmallest=!1;if(!$tableWrap.length)return!0;var tableW=$tableWrap.width();cmb.styleBreakPoint>tableW&&(isSmall=!0,isSmallest=cmb.styleBreakPoint-62>tableW),tableW=isSmall?tableW:Math.round(.82*$tableWrap.width()*.97);var newWidth=tableW-30;if(!isSmall||isSide||isSmallest||(newWidth-=75),newWidth>639)return!0;var $embeds=$this.find(".cmb-type-oembed .embed-status"),$children=$embeds.children().not(".cmb2-remove-wrapper");if(!$children.length)return!0;$children.each(function(){var $this=$(this),iwidth=$this.width(),iheight=$this.height(),_newWidth=newWidth;$this.parents(".cmb-repeat-row").length&&!isSmall&&(_newWidth=newWidth-91,_newWidth=785>tableW?_newWidth-15:_newWidth);var newHeight=Math.round(_newWidth*iheight/iwidth);$this.width(_newWidth).height(newHeight)})})},cmb.doAjax=function($obj){var oembed_url=$obj.val();if(!(oembed_url.length<6)){var field_id=$obj.attr("id"),$context=$obj.closest(".cmb-td"),$embed_container=$context.find(".embed-status"),$embed_wrap=$context.find(".embed_wrap"),$child_el=$embed_container.find(":first-child"),oembed_width=$embed_container.length&&$child_el.length?$child_el.width():$obj.width();cmb.log("oembed_url",oembed_url,field_id),cmb.spinner($context),$embed_wrap.html(""),setTimeout(function(){$(".cmb2-oembed:focus").val()===oembed_url&&$.ajax({type:"post",dataType:"json",url:l10n.ajaxurl,data:{action:"cmb2_oembed_handler",oembed_url:oembed_url,oembed_width:oembed_width>300?oembed_width:300,field_id:field_id,object_id:$obj.data("objectid"),object_type:$obj.data("objecttype"),cmb2_ajax_nonce:l10n.ajax_nonce},success:function(response){cmb.log(response),cmb.spinner($context,!0),$embed_wrap.html(response.data)}})},500)}},cmb.metabox=function(){return cmb.$metabox?cmb.$metabox:(cmb.$metabox=$(".cmb2-wrap > .cmb2-metabox"),cmb.$metabox)},cmb.spinner=function($context,hide){var m=hide?"removeClass":"addClass";$(".cmb-spinner",$context)[m]("is-active")},cmb.trigger=function(evtName){var args=Array.prototype.slice.call(arguments,1);args.push(cmb),$document.trigger(evtName,args)},cmb.triggerElement=function($el,evtName){var args=Array.prototype.slice.call(arguments,2);args.push(cmb),$el.trigger(evtName,args)},cmb.getFieldArg=function(hash,arg){return cmb.getField(hash)[arg]},cmb.getFields=function(filterCb){if("function"==typeof filterCb){var fields=[];return $.each(l10n.fields,function(hash,field){filterCb(field,hash)&&fields.push(field)}),fields}return l10n.fields},cmb.getField=function(hash){var field={};if(hash=hash instanceof jQuery?hash.data("hash"):hash)try{if(l10n.fields[hash])throw new Error(hash);cmb.getFields(function(field){if("function"==typeof hash){if(hash(field))throw new Error(field.hash)}else if(field.id&&field.id===hash)throw new Error(field.hash)})}catch(e){field=l10n.fields[e.message]}return field},cmb.log=function(){l10n.script_debug&&console&&"function"==typeof console.log&&console.log.apply(console,arguments)},cmb.replaceLast=function(string,search,replace){var n=string.lastIndexOf(search);return string.slice(0,n)+string.slice(n).replace(search,replace)},$(cmb.init)}(window,document,jQuery,window.CMB2),window.CMB2=window.CMB2||{},window.CMB2.wysiwyg=window.CMB2.wysiwyg||{},function(window,document,$,cmb,wysiwyg,undefined){"use strict";function delayedInit(){0===toBeDestroyed.length?toBeInitialized.forEach(function(toInit){toBeInitialized.splice(toBeInitialized.indexOf(toInit),1),wysiwyg.init.apply(wysiwyg,toInit)}):window.setTimeout(delayedInit,100)}function delayedDestroy(){toBeDestroyed.forEach(function(id){toBeDestroyed.splice(toBeDestroyed.indexOf(id),1),wysiwyg.destroy(id)})}function getGroupData(data){var groupid=data.groupid,fieldid=data.fieldid;return all[groupid]&&all[groupid][fieldid]||(all[groupid]=all[groupid]||{},all[groupid][fieldid]={template:wp.template("cmb2-wysiwyg-"+groupid+"-"+fieldid),defaults:{mce:$.extend({},tinyMCEPreInit.mceInit["cmb2_i_"+groupid+fieldid]),qt:$.extend({},tinyMCEPreInit.qtInit["cmb2_i_"+groupid+fieldid])}},delete tinyMCEPreInit.mceInit["cmb2_i_"+groupid+fieldid],delete tinyMCEPreInit.qtInit["cmb2_i_"+groupid+fieldid]),all[groupid][fieldid]}function initOptions(options){var prop,newSettings,newQTS,nameRegex=new RegExp("cmb2_n_"+options.groupid+options.fieldid,"g"),idRegex=new RegExp("cmb2_i_"+options.groupid+options.fieldid,"g");if(void 0===tinyMCEPreInit.mceInit[options.id]){newSettings=$.extend({},options.defaults.mce);for(prop in newSettings)"string"==typeof newSettings[prop]&&(newSettings[prop]=newSettings[prop].replace(idRegex,options.id).replace(nameRegex,options.name));tinyMCEPreInit.mceInit[options.id]=newSettings}if(void 0===tinyMCEPreInit.qtInit[options.id]){newQTS=$.extend({},options.defaults.qt);for(prop in newQTS)"string"==typeof newQTS[prop]&&(newQTS[prop]=newQTS[prop].replace(idRegex,options.id).replace(nameRegex,options.name));tinyMCEPreInit.qtInit[options.id]=newQTS}}var toBeDestroyed=[],toBeInitialized=[],all=wysiwyg.all={};wysiwyg.initAll=function(){var $this,data,initiated;$(".cmb2-wysiwyg-placeholder").each(function(){$this=$(this),data=$this.data(),data.groupid&&(data.id=$this.attr("id"),data.name=$this.attr("name"),data.value=$this.val(),wysiwyg.init($this,data,!1),initiated=!0)}),!0===initiated&&(void 0!==window.QTags&&window.QTags._buttonsInit(),$(document).on("cmb2_add_row",wysiwyg.addRow).on("cmb2_remove_group_row_start",wysiwyg.destroyRowEditors).on("cmb2_shift_rows_start",wysiwyg.shiftStart).on("cmb2_shift_rows_complete",wysiwyg.shiftComplete))},wysiwyg.addRow=function(evt,$row){wysiwyg.initRow($row,evt)},wysiwyg.destroyRowEditors=function(evt,$btn){wysiwyg.destroy($btn.parents(".cmb-repeatable-grouping").find(".wp-editor-area").attr("id"))},wysiwyg.shiftStart=function(evt,$btn,$from,$to){$from.add($to).find(".wp-editor-wrap textarea").each(function(){wysiwyg.destroy($(this).attr("id"))})},wysiwyg.shiftComplete=function(evt,$btn,$from,$to){$from.add($to).each(function(){wysiwyg.initRow($(this),evt)})},wysiwyg.initRow=function($row,evt){var $toReplace,data,defVal;$row.find(".cmb2-wysiwyg-inner-wrap").each(function(){$toReplace=$(this),data=$toReplace.data(),defVal=cmb.getFieldArg(data.hash,"default",""),defVal=void 0!==defVal&&!1!==defVal?defVal:"",data.iterator=$row.data("iterator"),data.fieldid=data.id,data.id=data.groupid+"_"+data.iterator+"_"+data.fieldid,data.name=data.groupid+"["+data.iterator+"]["+data.fieldid+"]",data.value="cmb2_add_row"!==evt.type&&$toReplace.find(".wp-editor-area").length?$toReplace.find(".wp-editor-area").val():defVal,0===toBeDestroyed.length?wysiwyg.init($toReplace,data):(toBeInitialized.push([$toReplace,data]),window.setTimeout(delayedInit,100))})},wysiwyg.init=function($toReplace,data,buttonsInit){if(!data.groupid)return!1;var mceActive=cmb.canTinyMCE(),qtActive="function"==typeof window.quicktags;$.extend(data,getGroupData(data)),initOptions(data),$toReplace.replaceWith(data.template(data)),mceActive&&window.tinyMCE.init(tinyMCEPreInit.mceInit[data.id]),qtActive&&window.quicktags(tinyMCEPreInit.qtInit[data.id]),mceActive&&$(document.getElementById(data.id)).parents(".wp-editor-wrap").removeClass("html-active").addClass("tmce-active"),!1!==buttonsInit&&void 0!==window.QTags&&window.QTags._buttonsInit()},wysiwyg.destroy=function(id){if(cmb.canTinyMCE()){var editor=tinyMCE.get(id);null!==editor&&void 0!==editor?(editor.destroy(),void 0===tinyMCEPreInit.mceInit[id]&&delete tinyMCEPreInit.mceInit[id],void 0===tinyMCEPreInit.qtInit[id]&&delete tinyMCEPreInit.qtInit[id]):-1===toBeDestroyed.indexOf(id)&&(toBeDestroyed.push(id),window.setTimeout(delayedDestroy,100))}},$(document).on("cmb_init",wysiwyg.initAll)}(window,document,jQuery,window.CMB2,window.CMB2.wysiwyg),window.CMB2=window.CMB2||{},window.CMB2.charcounter=window.CMB2.charcounter||{},function(window,document,$,cmb,counter){"use strict";if(!wp.utils||!wp.utils.WordCounter)return cmb.log("Cannot find wp.utils!");counter.counters={};var counters=counter.counters,wpCounter=new wp.utils.WordCounter;counter.updateCounter=function(field_id){if(!counters.hasOwnProperty(field_id))return null;var instance=counters[field_id],wysiwyg=instance.editor&&!instance.editor.isHidden(),text=wysiwyg?instance.editor.getContent({format:"raw"}):cmb.$id(field_id).val().trim(),count=wpCounter.count(text,instance.type),exceeded=instance.max&&count>instance.max,val=instance.max?instance.max-count:count;return instance.$el.parents(".cmb2-char-counter-wrap")[exceeded?"addClass":"removeClass"]("cmb2-max-exceeded"),instance.$el.val(val).outerWidth(8*String(val).length+15+"px"),count},counter.instantiate=function($el){var data=$el.data();if(!(data.fieldId in counters)){var instance={$el:$el,max:data.max,type:"words"===data.counterType?"words":"characters_including_spaces",editor:!1};counters[data.fieldId]=instance,counter.updateCounter(data.fieldId)}},counter.initAll=function(){$(".cmb2-char-counter").each(function(){counter.instantiate($(this))})},counter.initWysiwyg=function(evt,editor){editor.id in counters&&(counters[editor.id].editor=editor,editor.on("nodechange keyup",counter.countWysiwyg))},counter.addRow=function(evt,$row){$row.find(".cmb2-char-counter").each(function(){var $this=$(this),id=$this.attr("id"),field_id=id.replace(/^char-counter-/,"");$this.attr("data-field-id",field_id).data("field-id",field_id),counter.instantiate($this)})},counter.cleanCounters=function(){var field_id,remove=[];for(field_id in counters)document.getElementById(field_id)||remove.push(field_id);remove.length&&_.each(remove,function(field_id){delete counters[field_id]})},counter.countWysiwyg=_.throttle(function(evt){return evt.hasOwnProperty("element")?counter.updateCounter($(evt.element).data("id")):evt.hasOwnProperty("currentTarget")?counter.updateCounter($(evt.currentTarget).data("id")):void 0}),counter.countTextarea=_.throttle(function(evt){counter.updateCounter(evt.currentTarget.id)},400),$(document).on("cmb_init",counter.initAll).on("tinymce-editor-init",counter.initWysiwyg).on("cmb2_add_row",counter.addRow).on("cmb2_remove_row",counter.cleanCounters).on("input keyup",".cmb2-count-chars",counter.countTextarea)}(window,document,jQuery,window.CMB2,window.CMB2.charcounter);index.php000064400000000033151717007610006365 0ustar000&&"object"!=typeof timezoneList[0])for(;tzl>tzi;tzi++)tzv=timezoneList[tzi],timezoneList[tzi]={value:tzv,label:$.timepicker.timezoneOffsetString(tzv,tp_inst.support.iso8601)};return tp_inst._defaults.timezoneList=timezoneList,tp_inst.timezone=null!==tp_inst._defaults.timezone?$.timepicker.timezoneOffsetNumber(tp_inst._defaults.timezone):-1*(new Date).getTimezoneOffset(),tp_inst.hour=tp_inst._defaults.hourtp_inst._defaults.hourMax?tp_inst._defaults.hourMax:tp_inst._defaults.hour,tp_inst.minute=tp_inst._defaults.minutetp_inst._defaults.minuteMax?tp_inst._defaults.minuteMax:tp_inst._defaults.minute,tp_inst.second=tp_inst._defaults.secondtp_inst._defaults.secondMax?tp_inst._defaults.secondMax:tp_inst._defaults.second,tp_inst.millisec=tp_inst._defaults.millisectp_inst._defaults.millisecMax?tp_inst._defaults.millisecMax:tp_inst._defaults.millisec,tp_inst.microsec=tp_inst._defaults.microsectp_inst._defaults.microsecMax?tp_inst._defaults.microsecMax:tp_inst._defaults.microsec,tp_inst.ampm="",tp_inst.$input=$input,tp_inst._defaults.altField&&(tp_inst.$altInput=$(tp_inst._defaults.altField),tp_inst._defaults.altRedirectFocus===!0&&tp_inst.$altInput.css({cursor:"pointer"}).focus(function(){$input.trigger("focus")})),(0===tp_inst._defaults.minDate||0===tp_inst._defaults.minDateTime)&&(tp_inst._defaults.minDate=new Date),(0===tp_inst._defaults.maxDate||0===tp_inst._defaults.maxDateTime)&&(tp_inst._defaults.maxDate=new Date),void 0!==tp_inst._defaults.minDate&&tp_inst._defaults.minDate instanceof Date&&(tp_inst._defaults.minDateTime=new Date(tp_inst._defaults.minDate.getTime())),void 0!==tp_inst._defaults.minDateTime&&tp_inst._defaults.minDateTime instanceof Date&&(tp_inst._defaults.minDate=new Date(tp_inst._defaults.minDateTime.getTime())),void 0!==tp_inst._defaults.maxDate&&tp_inst._defaults.maxDate instanceof Date&&(tp_inst._defaults.maxDateTime=new Date(tp_inst._defaults.maxDate.getTime())),void 0!==tp_inst._defaults.maxDateTime&&tp_inst._defaults.maxDateTime instanceof Date&&(tp_inst._defaults.maxDate=new Date(tp_inst._defaults.maxDateTime.getTime())),tp_inst.$input.bind("focus",function(){tp_inst._onFocus()}),tp_inst},_addTimePicker:function(e){var t=this.$altInput&&this._defaults.altFieldTimeOnly?this.$input.val()+" "+this.$altInput.val():this.$input.val();this.timeDefined=this._parseTime(t),this._limitMinMaxDateTime(e,!1),this._injectTimePicker()},_parseTime:function(e,t){if(this.inst||(this.inst=$.datepicker._getInst(this.$input[0])),t||!this._defaults.timeOnly){var i=$.datepicker._get(this.inst,"dateFormat");try{var s=parseDateTimeInternal(i,this._defaults.timeFormat,e,$.datepicker._getFormatConfig(this.inst),this._defaults);if(!s.timeObj)return!1;$.extend(this,s.timeObj)}catch(a){return $.timepicker.log("Error parsing the date/time string: "+a+"\ndate/time string = "+e+"\ntimeFormat = "+this._defaults.timeFormat+"\ndateFormat = "+i),!1}return!0}var n=$.datepicker.parseTime(this._defaults.timeFormat,e,this._defaults);return n?($.extend(this,n),!0):!1},_injectTimePicker:function(){var e=this.inst.dpDiv,t=this.inst.settings,i=this,s="",a="",n=null,r={},l={},o=null,c=0,u=0;if(0===e.find("div.ui-timepicker-div").length&&t.showTimepicker){var m=' style="display:none;"',d='
    '+'
    "+t.timeText+"
    "+'
    ";for(c=0,u=this.units.length;u>c;c++){if(s=this.units[c],a=s.substr(0,1).toUpperCase()+s.substr(1),n=null!==t["show"+a]?t["show"+a]:this.support[s],r[s]=parseInt(t[s+"Max"]-(t[s+"Max"]-t[s+"Min"])%t["step"+a],10),l[s]=0,d+='
    "+t[s+"Text"]+"
    "+'
    ",n&&t[s+"Grid"]>0){if(d+='
    ',"hour"===s)for(var h=t[s+"Min"];r[s]>=h;h+=parseInt(t[s+"Grid"],10)){l[s]++;var p=$.datepicker.formatTime(this.support.ampm?"hht":"HH",{hour:h},t);d+='"}else for(var _=t[s+"Min"];r[s]>=_;_+=parseInt(t[s+"Grid"],10))l[s]++,d+='";d+="
    '+p+"'+(10>_?"0":"")+_+"
    "}d+="
    "}var f=null!==t.showTimezone?t.showTimezone:this.support.timezone;d+='
    "+t.timezoneText+"
    ",d+='
    ",d+="
    ";var g=$(d);for(t.timeOnly===!0&&(g.prepend('
    '+t.timeOnlyTitle+"
    "+"
    "),e.find(".ui-datepicker-header, .ui-datepicker-calendar").hide()),c=0,u=i.units.length;u>c;c++)s=i.units[c],a=s.substr(0,1).toUpperCase()+s.substr(1),n=null!==t["show"+a]?t["show"+a]:this.support[s],i[s+"_slider"]=i.control.create(i,g.find(".ui_tpicker_"+s+"_slider"),s,i[s],t[s+"Min"],r[s],t["step"+a]),n&&t[s+"Grid"]>0&&(o=100*l[s]*t[s+"Grid"]/(r[s]-t[s+"Min"]),g.find(".ui_tpicker_"+s+" table").css({width:o+"%",marginLeft:t.isRTL?"0":o/(-2*l[s])+"%",marginRight:t.isRTL?o/(-2*l[s])+"%":"0",borderCollapse:"collapse"}).find("td").click(function(){var e=$(this),t=e.html(),a=parseInt(t.replace(/[^0-9]/g),10),n=t.replace(/[^apm]/gi),r=e.data("for");"hour"===r&&(-1!==n.indexOf("p")&&12>a?a+=12:-1!==n.indexOf("a")&&12===a&&(a=0)),i.control.value(i,i[r+"_slider"],s,a),i._onTimeChange(),i._onSelectHandler()}).css({cursor:"pointer",width:100/l[s]+"%",textAlign:"center",overflow:"hidden"}));if(this.timezone_select=g.find(".ui_tpicker_timezone").append("").find("select"),$.fn.append.apply(this.timezone_select,$.map(t.timezoneList,function(e){return $("