Add massive collection of CSS, JavaScript and theme assets that were previously excluded: **CSS Files (681 total):** - HVAC plugin-specific styles (hvac-*.css): 34 files including dashboard, certificates, registration, mobile nav, accessibility fixes, animations, and welcome popup - Theme framework files (Astra, builder systems, layouts): 200+ files - Plugin compatibility styles (WooCommerce, WPForms, Elementor, Contact Form 7): 150+ files - WordPress core and editor styles: 50+ files - Responsive and RTL language support: 200+ files **JavaScript Files (400+ total):** - HVAC plugin functionality (hvac-*.js): 27 files including menu systems, dashboard enhancements, profile sharing, mobile responsive features, accessibility, and animations - Framework and library files: jQuery plugins, GSAP, AOS, Swiper, Chart.js, Lottie, Isotope - Plugin compatibility scripts: WPForms, WooCommerce, Elementor, Contact Form 7, LifterLMS - WordPress core functionality: customizer, admin, block editor compatibility - Third-party integrations: Stripe, SMTP, analytics, search functionality **Assets:** - Certificate background images and logos - Comprehensive theme styling infrastructure - Mobile-responsive design systems - Cross-browser compatibility assets - Performance-optimized minified versions **Updated .gitignore:** - Fixed asset directory whitelisting patterns to properly include CSS/JS/images - Added proper directory structure recognition (!/assets/css/, !/assets/js/, etc.) - Maintains security by excluding sensitive files while including essential assets This commit provides the complete frontend infrastructure needed for: - Full theme functionality and styling - Plugin feature implementations - Mobile responsiveness and accessibility - Cross-browser compatibility - Performance optimization - Developer workflow support
		
			
				
	
	
		
			534 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			534 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* 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 <style> tag.
 | |
| 			widgetContainer.style = '';
 | |
| 			$widgetStyles.text( '' );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Change style setting handler.
 | |
| 		 *
 | |
| 		 * @since 1.8.3
 | |
| 		 *
 | |
| 		 * @param {object} changedModel Changed model.
 | |
| 		 * @param {object} view         View.
 | |
| 		 */
 | |
| 		// eslint-disable-next-line complexity
 | |
| 		changeStyleSettings: function( changedModel, view ) {
 | |
| 
 | |
| 			let widgetContainer = view.$el[0];
 | |
| 			let parsedAtts      = changedModel.parseGlobalSettings( changedModel );
 | |
| 
 | |
| 			for ( let element in changedModel.changed ) {
 | |
| 
 | |
| 				if ( ! app.getStyleAttributesKeys().includes( element ) ) {
 | |
| 					view.allowRender = element !== 'copyPasteJsonValue';
 | |
| 					continue;
 | |
| 				}
 | |
| 
 | |
| 				view.allowRender = false;
 | |
| 				let attrValue    = app.getParsedValue( element, parsedAtts );
 | |
| 				let property     =  element.replace( /[A-Z]/g, letter => `-${letter.toLowerCase()}` );
 | |
| 				let borderRadiusItems = [ 'fieldBorderRadius', 'buttonBorderRadius' ];
 | |
| 
 | |
| 				if ( borderRadiusItems.includes( element ) ) {
 | |
| 					attrValue = attrValue + 'px';
 | |
| 				}
 | |
| 
 | |
| 				switch ( property ) {
 | |
| 					case 'field-size':
 | |
| 					case 'label-size':
 | |
| 					case 'button-size':
 | |
| 						for ( const key in wpformsElementorVars.sizes[ property ][ attrValue ] ) {
 | |
| 							widgetContainer.style.setProperty(
 | |
| 								`--wpforms-${property}-${key}`,
 | |
| 								wpformsElementorVars.sizes[ property ][ attrValue ][ key ],
 | |
| 							);
 | |
| 						}
 | |
| 
 | |
| 						break;
 | |
| 
 | |
| 					default:
 | |
| 						widgetContainer.style.setProperty( `--wpforms-${property}`, attrValue );
 | |
| 				}
 | |
| 			}
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Copy/paste widget settings.
 | |
| 		 *
 | |
| 		 * @since 1.8.3
 | |
| 		 *
 | |
| 		 * @param {object} model Settings model.
 | |
| 		 */
 | |
| 		updateCopyPasteContent: function( model ) {
 | |
| 
 | |
| 			let styleSettings = app.getStyleAttributesKeys();
 | |
| 			let content       = {};
 | |
| 			let atts          = model.parseGlobalSettings( model );
 | |
| 
 | |
| 			styleSettings.forEach( function( element ) {
 | |
| 				content[element] = app.getParsedValue( element, atts );
 | |
| 			} );
 | |
| 
 | |
| 			model.setExternalChange( 'copyPasteJsonValue', JSON.stringify( content ) );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Reset global style settings.
 | |
| 		 *
 | |
| 		 * @since 1.8.7
 | |
| 		 *
 | |
| 		 * @param {Object} model     Settings model.
 | |
| 		 * @param {Object} container Container.
 | |
| 		 */
 | |
| 		resetGlobalStyleSettings( model, container ) {
 | |
| 			const globals = model.get( '__globals__' );
 | |
| 
 | |
| 			if ( globals && ! model.changed.__globals__ ) {
 | |
| 				elementorCommon.api.run( 'document/globals/settings', {
 | |
| 					container,
 | |
| 					settings: {},
 | |
| 					options: {
 | |
| 						external: true,
 | |
| 						render: false,
 | |
| 					},
 | |
| 				} );
 | |
| 			}
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Paste settings.
 | |
| 		 *
 | |
| 		 * @since 1.8.3
 | |
| 		 *
 | |
| 		 * @param {Object} model Settings model.
 | |
| 		 * @param {Object} view  View.
 | |
| 		 */
 | |
| 		pasteSettings( model, view ) {
 | |
| 			const copyPasteJsonValue = model.changed.copyPasteJsonValue;
 | |
| 			const pasteAttributes = app.parseValidateJson( copyPasteJsonValue );
 | |
| 			const container = view.container;
 | |
| 
 | |
| 			if ( ! pasteAttributes ) {
 | |
| 				if ( copyPasteJsonValue ) {
 | |
| 					elementorCommon.dialogsManager.createWidget( 'alert', {
 | |
| 						message: wpformsElementorVars.strings.copy_paste_error,
 | |
| 						headerMessage: wpformsElementorVars.strings.heads_up,
 | |
| 					} ).show();
 | |
| 				}
 | |
| 
 | |
| 				this.updateCopyPasteContent( model );
 | |
| 
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			app.resetGlobalStyleSettings( model, container );
 | |
| 
 | |
| 			model.set( pasteAttributes );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Parse and validate JSON string.
 | |
| 		 *
 | |
| 		 * @since 1.8.3
 | |
| 		 *
 | |
| 		 * @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 );
 | |
| 			} catch ( error ) {
 | |
| 				atts = false;
 | |
| 			}
 | |
| 
 | |
| 			return atts;
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Get list of the style attributes keys.
 | |
| 		 *
 | |
| 		 * @since 1.8.3
 | |
| 		 *
 | |
| 		 * @returns {Array} Style attributes keys.
 | |
| 		 */
 | |
| 		getStyleAttributesKeys: function() {
 | |
| 
 | |
| 			return [
 | |
| 				'fieldSize',
 | |
| 				'fieldBorderRadius',
 | |
| 				'fieldBackgroundColor',
 | |
| 				'fieldBorderColor',
 | |
| 				'fieldTextColor',
 | |
| 				'labelSize',
 | |
| 				'labelColor',
 | |
| 				'labelSublabelColor',
 | |
| 				'labelErrorColor',
 | |
| 				'buttonSize',
 | |
| 				'buttonBorderRadius',
 | |
| 				'buttonBackgroundColor',
 | |
| 				'buttonTextColor',
 | |
| 			];
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Get parsed attribute value.
 | |
| 		 *
 | |
| 		 * @since 1.8.3
 | |
| 		 *
 | |
| 		 * @param {string} attrName   Attribute name.
 | |
| 		 * @param {object} parsedAtts Parsed attributes.
 | |
| 		 *
 | |
| 		 * @returns {string} Attribute value.
 | |
| 		 */
 | |
| 		getParsedValue: function( attrName, parsedAtts ) {
 | |
| 
 | |
| 			let rawValue = parsedAtts[ attrName ];
 | |
| 			let value;
 | |
| 
 | |
| 			if ( typeof rawValue === 'undefined' ) {
 | |
| 				value = false;
 | |
| 			} else if ( typeof rawValue === 'object' && Object.prototype.hasOwnProperty.call( rawValue, 'value' ) ) {
 | |
| 				value = rawValue.value;
 | |
| 			} else {
 | |
| 				value = rawValue;
 | |
| 			}
 | |
| 
 | |
| 			return value;
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Initialize RichText field.
 | |
| 		 *
 | |
| 		 * @since 1.8.3
 | |
| 		 *
 | |
| 		 * @param {int} formId Form ID.
 | |
| 		 */
 | |
| 		initRichTextField: function( formId ) {
 | |
| 
 | |
| 			// Set default tab to `Visual`.
 | |
| 			$( `#wpforms-${formId} .wp-editor-wrap` ).removeClass( 'html-active' ).addClass( 'tmce-active' );
 | |
| 		},
 | |
| 
 | |
| 
 | |
| 		/**
 | |
| 		 * Update accent colors of some fields in Elementor widget.
 | |
| 		 *
 | |
| 		 * @since 1.8.3
 | |
| 		 *
 | |
| 		 * @param {jQuery}  widgetContainer Widget container.
 | |
| 		 * @param {integer} formId          Event details object.
 | |
| 		 */
 | |
| 		updateAccentColors: function( widgetContainer, formId ) {
 | |
| 
 | |
| 			const $form = widgetContainer.find( `#wpforms-${formId}` ),
 | |
| 				FrontendModern = window.WPForms.FrontendModern;
 | |
| 
 | |
| 			FrontendModern.updateGBBlockPageIndicatorColor( $form );
 | |
| 			FrontendModern.updateGBBlockIconChoicesColor( $form );
 | |
| 			FrontendModern.updateGBBlockRatingColor( $form );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Init Modern style Dropdown fields (<select>).
 | |
| 		 *
 | |
| 		 * @since 1.8.3
 | |
| 		 *
 | |
| 		 * @param {jQuery}  widgetContainer Widget container.
 | |
| 		 * @param {integer} formId          Form id.
 | |
| 		 */
 | |
| 		loadChoicesJS: function( widgetContainer, formId ) {
 | |
| 
 | |
| 			if ( typeof window.Choices !== 'function' ) {
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			const $form = widgetContainer.find( `#wpforms-${formId}` );
 | |
| 
 | |
| 			$form.find( '.choicesjs-select' ).each( function( idx, el ) {
 | |
| 
 | |
| 				const $el = $( el );
 | |
| 
 | |
| 				if ( $el.data( 'choice' ) === 'active' ) {
 | |
| 					return;
 | |
| 				}
 | |
| 
 | |
| 				var args = window.wpforms_choicesjs_config || {},
 | |
| 					searchEnabled = $el.data( 'search-enabled' ),
 | |
| 					$field = $el.closest( '.wpforms-field' );
 | |
| 
 | |
| 				args.searchEnabled  = 'undefined' !== typeof searchEnabled ? searchEnabled : true;
 | |
| 				args.callbackOnInit = function() {
 | |
| 
 | |
| 					var self = this,
 | |
| 						$element = $( self.passedElement.element ),
 | |
| 						$input = $( self.input.element ),
 | |
| 						sizeClass = $element.data( 'size-class' );
 | |
| 
 | |
| 					// 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 back.
 | |
| 					 */
 | |
| 					if ( $element.prop( 'multiple' ) ) {
 | |
| 
 | |
| 						// On init event.
 | |
| 						$input.data( 'placeholder', $input.attr( 'placeholder' ) );
 | |
| 
 | |
| 						if ( self.getValue( true ).length ) {
 | |
| 							$input.removeAttr( 'placeholder' );
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					this.disable();
 | |
| 					$field.find( '.is-disabled' ).removeClass( 'is-disabled' );
 | |
| 				};
 | |
| 
 | |
| 				try {
 | |
| 					const choicesInstance =  new Choices( el, args );
 | |
| 
 | |
| 					// Save Choices.js instance for future access.
 | |
| 					$el.data( 'choicesjs', choicesInstance );
 | |
| 
 | |
| 				} catch ( e ) {} // eslint-disable-line no-empty
 | |
| 			} );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Initialize Repeater field.
 | |
| 		 *
 | |
| 		 * @since 1.8.9
 | |
| 		 *
 | |
| 		 * @param {number} formId Form ID.
 | |
| 		 */
 | |
| 		initRepeaterField( formId ) {
 | |
| 			const $rowButtons = $( `.wpforms-form[data-formid="${ formId }"] .wpforms-field-repeater > .wpforms-field-repeater-display-rows .wpforms-field-repeater-display-rows-buttons` );
 | |
| 
 | |
| 			// Get the label height and set the button position.
 | |
| 			$rowButtons.each( function() {
 | |
| 				const $cont = $( this );
 | |
| 				const $label = $cont.siblings( '.wpforms-layout-column' )
 | |
| 					.find( '.wpforms-field' ).first()
 | |
| 					.find( '.wpforms-field-label' );
 | |
| 				const labelStyle = window.getComputedStyle( $label.get( 0 ) );
 | |
| 				const margin = labelStyle?.getPropertyValue( '--wpforms-field-size-input-spacing' ) || 0;
 | |
| 				const height = $label.outerHeight() || 0;
 | |
| 				const top = height + parseInt( margin, 10 ) + 10;
 | |
| 
 | |
| 				$cont.css( { top } );
 | |
| 			} );
 | |
| 
 | |
| 			// Init buttons and descriptions for each repeater in each form.
 | |
| 			$( `.wpforms-form[data-formid="${ formId }"]` ).each( function() {
 | |
| 				const $repeater = $( this ).find( '.wpforms-field-repeater' );
 | |
| 
 | |
| 				$repeater.find( '.wpforms-field-repeater-display-rows-buttons' ).addClass( 'wpforms-init' );
 | |
| 				$repeater.find( '.wpforms-field-repeater-display-rows:last .wpforms-field-description' ).addClass( 'wpforms-init' );
 | |
| 			} );
 | |
| 		},
 | |
| 	};
 | |
| 
 | |
| 	return app;
 | |
| 
 | |
| }( document, window, jQuery ) );
 | |
| 
 | |
| // Initialize.
 | |
| WPFormsElementorModern.init();
 |