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
		
			
				
	
	
		
			920 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			920 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* global wpforms_challenge_admin */
 | |
| /**
 | |
|  * WPForms Challenge function.
 | |
|  *
 | |
|  * @since 1.5.0
 | |
|  * @since 1.6.2 Challenge v2
 | |
|  */
 | |
| 'use strict';
 | |
| 
 | |
| var WPFormsChallenge = window.WPFormsChallenge || {};
 | |
| 
 | |
| WPFormsChallenge.core = window.WPFormsChallenge.core || ( function( document, window, $ ) {
 | |
| 
 | |
| 	/**
 | |
| 	 * Public functions and properties.
 | |
| 	 *
 | |
| 	 * @since 1.5.0
 | |
| 	 *
 | |
| 	 * @type {object}
 | |
| 	 */
 | |
| 	var app = {};
 | |
| 
 | |
| 	/**
 | |
| 	 * Runtime variables.
 | |
| 	 *
 | |
| 	 * @since 1.6.2
 | |
| 	 *
 | |
| 	 * @type {object}
 | |
| 	 */
 | |
| 	var vars = {};
 | |
| 
 | |
| 	/**
 | |
| 	 * DOM elements.
 | |
| 	 *
 | |
| 	 * @since 1.6.2
 | |
| 	 *
 | |
| 	 * @type {object}
 | |
| 	 */
 | |
| 	var el = {};
 | |
| 
 | |
| 	/**
 | |
| 	 * Timer functions and properties.
 | |
| 	 *
 | |
| 	 * @since 1.5.0
 | |
| 	 *
 | |
| 	 * @type {object}
 | |
| 	 */
 | |
| 	var timer = {
 | |
| 
 | |
| 		/**
 | |
| 		 * Number of minutes to complete the challenge.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @type {number}
 | |
| 		 */
 | |
| 		initialSecondsLeft: WPFormsChallenge.admin.l10n.minutes_left * 60,
 | |
| 
 | |
| 		/**
 | |
| 		 * Load timer ID.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @returns {string} ID from setInterval().
 | |
| 		 */
 | |
| 		loadId: function() {
 | |
| 
 | |
| 			return localStorage.getItem( 'wpformsChallengeTimerId' );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Save timer ID.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @param {number|string} id setInterval() ID to save.
 | |
| 		 */
 | |
| 		saveId: function( id ) {
 | |
| 
 | |
| 			localStorage.setItem( 'wpformsChallengeTimerId', id );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Run the timer.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @param {number} secondsLeft Number of seconds left to complete the Challenge.
 | |
| 		 *
 | |
| 		 * @returns {string|void} ID from setInterval().
 | |
| 		 */
 | |
| 		run: function( secondsLeft ) {
 | |
| 
 | |
| 			if ( 5 === app.loadStep() ) {
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			var timerId = setInterval( function() {
 | |
| 
 | |
| 				app.updateTimerUI( secondsLeft );
 | |
| 				secondsLeft--;
 | |
| 				if ( 0 > secondsLeft ) {
 | |
| 					timer.saveSecondsLeft( 0 );
 | |
| 					clearInterval( timerId );
 | |
| 				}
 | |
| 			}, 1000 );
 | |
| 
 | |
| 			timer.saveId( timerId );
 | |
| 
 | |
| 			return timerId;
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Pause the timer.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 */
 | |
| 		pause: function() {
 | |
| 
 | |
| 			var timerId;
 | |
| 			var elSeconds;
 | |
| 			var secondsLeft = timer.getSecondsLeft();
 | |
| 
 | |
| 			if ( 0 === secondsLeft || 5 === app.loadStep() ) {
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			timerId = timer.loadId();
 | |
| 			clearInterval( timerId );
 | |
| 
 | |
| 			elSeconds = $( '#wpforms-challenge-timer' ).data( 'seconds-left' );
 | |
| 
 | |
| 			if ( elSeconds ) {
 | |
| 				timer.saveSecondsLeft( elSeconds );
 | |
| 			}
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Resume the timer.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 */
 | |
| 		resume: function() {
 | |
| 
 | |
| 			var timerId;
 | |
| 			var secondsLeft = timer.getSecondsLeft();
 | |
| 
 | |
| 			if ( 0 === secondsLeft || 5 === app.loadStep() ) {
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			timerId = timer.loadId();
 | |
| 
 | |
| 			if ( timerId ) {
 | |
| 				clearInterval( timerId );
 | |
| 			}
 | |
| 
 | |
| 			timer.run( secondsLeft );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Clear all frontend saved timer data.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 */
 | |
| 		clear: function() {
 | |
| 
 | |
| 			localStorage.removeItem( 'wpformsChallengeSecondsLeft' );
 | |
| 			localStorage.removeItem( 'wpformsChallengeTimerId' );
 | |
| 			localStorage.removeItem( 'wpformsChallengeTimerStatus' );
 | |
| 			$( '#wpforms-challenge-timer' ).removeData( 'seconds-left' );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Get number of seconds left to complete the Challenge.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @returns {number} Number of seconds left to complete the Challenge.
 | |
| 		 */
 | |
| 		getSecondsLeft: function() {
 | |
| 
 | |
| 			var secondsLeft = localStorage.getItem( 'wpformsChallengeSecondsLeft' );
 | |
| 			secondsLeft = parseInt( secondsLeft, 10 ) || 0;
 | |
| 
 | |
| 			return secondsLeft;
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Get number of seconds spent completing the Challenge.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @param {number} secondsLeft Number of seconds left to complete the Challenge.
 | |
| 		 *
 | |
| 		 * @returns {number} Number of seconds spent completing the Challenge.
 | |
| 		 */
 | |
| 		getSecondsSpent: function( secondsLeft ) {
 | |
| 
 | |
| 			secondsLeft = secondsLeft || timer.getSecondsLeft();
 | |
| 
 | |
| 			return timer.initialSecondsLeft - secondsLeft;
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Save number of seconds left to complete the Challenge.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @param {number|string} secondsLeft Number of seconds left to complete the Challenge.
 | |
| 		 */
 | |
| 		saveSecondsLeft: function( secondsLeft ) {
 | |
| 
 | |
| 			localStorage.setItem( 'wpformsChallengeSecondsLeft', secondsLeft );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Get 'minutes' part of timer display.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @param {number} secondsLeft Number of seconds left to complete the Challenge.
 | |
| 		 *
 | |
| 		 * @returns {number} 'Minutes' part of timer display.
 | |
| 		 */
 | |
| 		getMinutesFormatted: function( secondsLeft ) {
 | |
| 
 | |
| 			secondsLeft = secondsLeft || timer.getSecondsLeft();
 | |
| 
 | |
| 			return Math.floor( secondsLeft / 60 );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Get 'seconds' part of timer display.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @param {number} secondsLeft Number of seconds left to complete the Challenge.
 | |
| 		 *
 | |
| 		 * @returns {number} 'Seconds' part of timer display.
 | |
| 		 */
 | |
| 		getSecondsFormatted: function( secondsLeft ) {
 | |
| 
 | |
| 			secondsLeft = secondsLeft || timer.getSecondsLeft();
 | |
| 
 | |
| 			return secondsLeft % 60;
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Get formatted timer for display.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @param {number} secondsLeft Number of seconds left to complete the Challenge.
 | |
| 		 *
 | |
| 		 * @returns {string} Formatted timer for display.
 | |
| 		 */
 | |
| 		getFormatted: function( secondsLeft ) {
 | |
| 
 | |
| 			secondsLeft = secondsLeft || timer.getSecondsLeft();
 | |
| 
 | |
| 			var timerMinutes = timer.getMinutesFormatted( secondsLeft );
 | |
| 			var timerSeconds = timer.getSecondsFormatted( secondsLeft );
 | |
| 
 | |
| 			return timerMinutes + ( 9 < timerSeconds ? ':' : ':0' ) + timerSeconds;
 | |
| 		},
 | |
| 	};
 | |
| 
 | |
| 	/**
 | |
| 	 * Public functions and properties.
 | |
| 	 */
 | |
| 	app = {
 | |
| 
 | |
| 		/**
 | |
| 		 * Public timer functions and properties.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 */
 | |
| 		timer: timer,
 | |
| 
 | |
| 		/**
 | |
| 		 * Start the engine.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 */
 | |
| 		init: function() {
 | |
| 
 | |
| 			$( app.ready );
 | |
| 			$( window ).on( 'load', function() {
 | |
| 
 | |
| 				// in case of jQuery 3.+ we need to wait for an `ready` event first.
 | |
| 				if ( typeof $.ready.then === 'function' ) {
 | |
| 					$.ready.then( app.load );
 | |
| 				} else {
 | |
| 					app.load();
 | |
| 				}
 | |
| 			} );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Document ready.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 */
 | |
| 		ready: function() {
 | |
| 
 | |
| 			app.setup();
 | |
| 			app.events();
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Window load.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 */
 | |
| 		load: function() {
 | |
| 
 | |
| 			if ( wpforms_challenge_admin.option.status === 'started' ) {
 | |
| 				app.timer.run( app.timer.getSecondsLeft() );
 | |
| 			}
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Initial setup.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 */
 | |
| 		setup: function() {
 | |
| 
 | |
| 			var secondsLeft;
 | |
| 			var timerId = app.timer.loadId();
 | |
| 
 | |
| 			if ( timerId ) {
 | |
| 				clearInterval( timerId );
 | |
| 				secondsLeft = app.timer.getSecondsLeft();
 | |
| 			}
 | |
| 
 | |
| 			if ( ! timerId || 0 === app.loadStep() || wpforms_challenge_admin.option.status === 'inited' ) {
 | |
| 				secondsLeft = app.timer.initialSecondsLeft;
 | |
| 			}
 | |
| 
 | |
| 			app.initElements();
 | |
| 			app.refreshStep();
 | |
| 			app.initListUI( null, true );
 | |
| 			app.updateListUI();
 | |
| 			app.updateTimerUI( secondsLeft );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Register JS events.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 */
 | |
| 		events: function() {
 | |
| 
 | |
| 			$( [ window, document ] )
 | |
| 				.on( 'blur', app.pauseChallenge )
 | |
| 				.on( 'focus', app.resumeChallenge )
 | |
| 				.on( 'click', '.wpforms-challenge-done-btn', app.resumeChallenge );
 | |
| 
 | |
| 			el.$btnPause.on( 'click', app.pauseChallenge );
 | |
| 			el.$btnResume.on( 'click', app.resumeChallenge );
 | |
| 
 | |
| 			el.$listSteps.on( 'click', '.wpforms-challenge-item-current', app.refreshPage );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * DOM elements.
 | |
| 		 *
 | |
| 		 * @since 1.6.2
 | |
| 		 */
 | |
| 		initElements: function() {
 | |
| 
 | |
| 			el = {
 | |
| 				$challenge:     $( '.wpforms-challenge' ),
 | |
| 				$btnPause:      $( '.wpforms-challenge-pause' ),
 | |
| 				$btnResume:     $( '.wpforms-challenge-resume' ),
 | |
| 				$listSteps:     $( '.wpforms-challenge-list' ),
 | |
| 				$listBlock:     $( '.wpforms-challenge-list-block' ),
 | |
| 				$listBtnToggle: $( '.wpforms-challenge-list-block .toggle-list' ),
 | |
| 				$progressBar:   $( '.wpforms-challenge-bar' ),
 | |
| 				$tooltipBtnDone: function() {
 | |
| 					return $( '.wpforms-challenge-tooltip .wpforms-challenge-done-btn' );
 | |
| 				},
 | |
| 			};
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Get last saved step.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @returns {number} Last saved step.
 | |
| 		 */
 | |
| 		loadStep: function() {
 | |
| 
 | |
| 			var step = localStorage.getItem( 'wpformsChallengeStep' );
 | |
| 			step = parseInt( step, 10 ) || 0;
 | |
| 
 | |
| 			return step;
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Save Challenge step.
 | |
| 		 *
 | |
| 		 * @param {number|string} step Step to save.
 | |
| 		 *
 | |
| 		 * @returns {object} jqXHR object from saveChallengeOption().
 | |
| 		 */
 | |
| 		saveStep: function( step ) {
 | |
| 
 | |
| 			localStorage.setItem( 'wpformsChallengeStep', step );
 | |
| 
 | |
| 			return WPFormsChallenge.admin.saveChallengeOption( { step: step } );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Update a step with backend data.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 */
 | |
| 		refreshStep: function() {
 | |
| 
 | |
| 			var savedStep = el.$challenge.data( 'wpforms-challenge-saved-step' );
 | |
| 			savedStep = parseInt( savedStep, 10 ) || 0;
 | |
| 
 | |
| 			// Step saved on a backend has a priority.
 | |
| 			if ( app.loadStep() !== savedStep ) {
 | |
| 				app.saveStep( savedStep );
 | |
| 			}
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Complete Challenge step.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @param {number|string} step Step to complete.
 | |
| 		 *
 | |
| 		 * @returns {object} jqXHR object from saveStep().
 | |
| 		 */
 | |
| 		stepCompleted: function( step ) {
 | |
| 
 | |
| 			app.updateListUI( step );
 | |
| 			app.updateTooltipUI( step );
 | |
| 
 | |
| 			return app.saveStep( step );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Initialize Challenge tooltips.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @param {number|string} step   Last saved step.
 | |
| 		 * @param {string}        anchor Element selector to bind tooltip to.
 | |
| 		 * @param {object}        args   Tooltipster arguments.
 | |
| 		 */
 | |
| 		initTooltips: function( step, anchor, args ) {
 | |
| 
 | |
| 			if ( typeof $.fn.tooltipster === 'undefined' ) {
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			var $dot = $( '<span class="wpforms-challenge-dot wpforms-challenge-dot-step' + step + '" data-wpforms-challenge-step="' + step + '"> </span>' );
 | |
| 			var tooltipsterArgs = {
 | |
| 				content          : $( '#tooltip-content' + step ),
 | |
| 				trigger          : null,
 | |
| 				interactive      : true,
 | |
| 				animationDuration: 0,
 | |
| 				delay            : 0,
 | |
| 				theme            : [ 'tooltipster-default', 'wpforms-challenge-tooltip' ],
 | |
| 				side             : [ 'top' ],
 | |
| 				distance         : 3,
 | |
| 				functionReady    : function( instance, helper ) {
 | |
| 
 | |
| 					$( helper.tooltip ).addClass( 'wpforms-challenge-tooltip-step' + step );
 | |
| 
 | |
| 					// Custom positioning.
 | |
| 					if ( step === 4 || step === 3 ) {
 | |
| 						instance.option( 'side', 'right' );
 | |
| 					} else if ( step === 1 ) {
 | |
| 						instance.option( 'side', 'left' );
 | |
| 					}
 | |
| 
 | |
| 					// Reposition is needed to render max-width CSS correctly.
 | |
| 					instance.reposition();
 | |
| 				},
 | |
| 			};
 | |
| 
 | |
| 			if ( typeof args === 'object' && args !== null ) {
 | |
| 				$.extend( tooltipsterArgs, args );
 | |
| 			}
 | |
| 
 | |
| 			$dot.insertAfter( anchor ).tooltipster( tooltipsterArgs );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Update tooltips appearance.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @param {number|string} step Last saved step.
 | |
| 		 */
 | |
| 		updateTooltipUI: function( step ) {
 | |
| 
 | |
| 			var nextStep;
 | |
| 
 | |
| 			step = step || app.loadStep();
 | |
| 			nextStep = step + 1;
 | |
| 
 | |
| 			$( '.wpforms-challenge-dot' ).each( function( i, el ) {
 | |
| 
 | |
| 				var $dot = $( el ),
 | |
| 					elStep = $dot.data( 'wpforms-challenge-step' );
 | |
| 
 | |
| 				if ( elStep < nextStep ) {
 | |
| 					$dot.addClass( 'wpforms-challenge-dot-completed' );
 | |
| 				}
 | |
| 
 | |
| 				if ( elStep > nextStep ) {
 | |
| 					$dot.addClass( 'wpforms-challenge-dot-next' );
 | |
| 				}
 | |
| 
 | |
| 				if ( elStep === nextStep ) {
 | |
| 					$dot.removeClass( 'wpforms-challenge-dot-completed wpforms-challenge-dot-next' );
 | |
| 				}
 | |
| 
 | |
| 				// Zero timeout is needed to properly detect $el visibility.
 | |
| 				setTimeout( function() {
 | |
| 					if ( $dot.is( ':visible' ) && elStep === nextStep ) {
 | |
| 						$dot.tooltipster( 'open' );
 | |
| 					} else {
 | |
| 						$dot.tooltipster( 'close' );
 | |
| 					}
 | |
| 				}, 0 );
 | |
| 			} );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Init ListUI.
 | |
| 		 *
 | |
| 		 * @since 1.6.2
 | |
| 		 *
 | |
| 		 * @param {number|string} status  Challenge status.
 | |
| 		 * @param {boolean}       initial Initial run, false by default.
 | |
| 		 */
 | |
| 		initListUI: function( status, initial ) {
 | |
| 
 | |
| 			status = status || wpforms_challenge_admin.option.status;
 | |
| 
 | |
| 			if ( [ 'started', 'paused' ].indexOf( status ) > -1 ) {
 | |
| 				el.$listBlock.find( 'p' ).hide();
 | |
| 				el.$listBtnToggle.show();
 | |
| 				el.$progressBar.show();
 | |
| 
 | |
| 				// Transform skip button to cancel button.
 | |
| 				var $skipBtn = el.$listBlock.find( '.list-block-button.challenge-skip' );
 | |
| 
 | |
| 				$skipBtn
 | |
| 					.attr( 'title', $skipBtn.data( 'cancel-title' ) )
 | |
| 					.removeClass( 'challenge-skip' )
 | |
| 					.addClass( 'challenge-cancel' );
 | |
| 			}
 | |
| 
 | |
| 			// Set initial window closed (collapsed) state if window is short or if it is closed manually.
 | |
| 			if (
 | |
| 				initial &&
 | |
| 				(
 | |
| 					( $( window ).height() < 900 && wpforms_challenge_admin.option.window_closed === '' ) ||
 | |
| 					wpforms_challenge_admin.option.window_closed === '1'
 | |
| 				)
 | |
| 			) {
 | |
| 				el.$listBlock.find( 'p' ).hide();
 | |
| 				el.$listBtnToggle.trigger( 'click' );
 | |
| 			}
 | |
| 
 | |
| 			if ( status === 'paused' ) {
 | |
| 
 | |
| 				el.$challenge.addClass( 'paused' );
 | |
| 				el.$btnPause.hide();
 | |
| 				el.$btnResume.show();
 | |
| 
 | |
| 			} else {
 | |
| 
 | |
| 				// Zero timeout is needed to avoid firing 'focus' and 'click' events in the same loop.
 | |
| 				setTimeout( function() {
 | |
| 					el.$btnPause.show();
 | |
| 				}, 0 );
 | |
| 
 | |
| 				el.$challenge.removeClass( 'paused' );
 | |
| 				el.$btnResume.hide();
 | |
| 
 | |
| 			}
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Update Challenge task list appearance.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @param {number|string} step Last saved step.
 | |
| 		 */
 | |
| 		updateListUI: function( step ) {
 | |
| 
 | |
| 			step = step || app.loadStep();
 | |
| 
 | |
| 			el.$listSteps.find( 'li' ).slice( 0, step ).addClass( 'wpforms-challenge-item-completed' ).removeClass( 'wpforms-challenge-item-current' );
 | |
| 			el.$listSteps.find( 'li' ).eq( step ).addClass( 'wpforms-challenge-item-current' );
 | |
| 			el.$progressBar.find( 'div' ).css( 'width', ( step * 20 ) + '%' );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Update Challenge timer appearance.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @param {number} secondsLeft Number of seconds left to complete the Challenge.
 | |
| 		 */
 | |
| 		updateTimerUI: function( secondsLeft ) {
 | |
| 
 | |
| 			if ( ! secondsLeft || isNaN( secondsLeft ) || '0' === secondsLeft ) {
 | |
| 				secondsLeft = 0;
 | |
| 			}
 | |
| 
 | |
| 			app.timer.saveSecondsLeft( secondsLeft );
 | |
| 			$( '#wpforms-challenge-timer' ).text( app.timer.getFormatted( secondsLeft ) ).data( 'seconds-left', secondsLeft );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Remove Challenge interface.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 */
 | |
| 		removeChallengeUI: function() {
 | |
| 
 | |
| 			$( '.wpforms-challenge-dot' ).remove();
 | |
| 			el.$challenge.remove();
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Clear all Challenge frontend saved data.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 */
 | |
| 		clearLocalStorage: function() {
 | |
| 
 | |
| 			localStorage.removeItem( 'wpformsChallengeStep' );
 | |
| 			app.timer.clear();
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Pause Challenge.
 | |
| 		 *
 | |
| 		 * @since 1.6.2
 | |
| 		 *
 | |
| 		 * @param {object} e Event object.
 | |
| 		 */
 | |
| 		pauseChallenge: function( e ) {
 | |
| 
 | |
| 			// Skip if out to the iframe.
 | |
| 			if ( document.activeElement.tagName === 'IFRAME' ) {
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			// Skip if is not started.
 | |
| 			if ( wpforms_challenge_admin.option.status !== 'started' ) {
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			vars.pauseEvent = e.type;
 | |
| 
 | |
| 			app.pauseResumeChallenge( 'pause' );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Resume Challenge.
 | |
| 		 *
 | |
| 		 * @since 1.6.2
 | |
| 		 *
 | |
| 		 * @param {object} e Event object.
 | |
| 		 *
 | |
| 		 * @returns {Function|void} Return pause challenge function or void.
 | |
| 		 */
 | |
| 		resumeChallenge: function( e ) {
 | |
| 
 | |
| 			// Skip if is not paused.
 | |
| 			if ( wpforms_challenge_admin.option.status !== 'paused' ) {
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			// Resume on 'focus' only if it has been paused on 'blur'.
 | |
| 			if ( e.type === 'focus' && vars.pauseEvent !== 'blur' ) {
 | |
| 				delete vars.pauseEvent;
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			vars.resumeEvent = e.type;
 | |
| 
 | |
| 			return app.pauseResumeChallenge( 'resume' );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Pause/Resume Challenge.
 | |
| 		 *
 | |
| 		 * @since 1.6.2
 | |
| 		 *
 | |
| 		 * @param {string} action Action to perform. `pause` or `resume`.
 | |
| 		 *
 | |
| 		 * @returns {Function} Save challenge option.
 | |
| 		 */
 | |
| 		pauseResumeChallenge: function( action ) {
 | |
| 
 | |
| 			action = action === 'pause' ? action : 'resume';
 | |
| 
 | |
| 			app.timer[ action ]();
 | |
| 
 | |
| 			var optionData = {
 | |
| 				status       : action === 'pause' ? 'paused' : 'started',
 | |
| 				seconds_spent: app.timer.getSecondsSpent(),
 | |
| 				seconds_left : app.timer.getSecondsLeft(),
 | |
| 			};
 | |
| 
 | |
| 			app.initListUI( optionData.status );
 | |
| 
 | |
| 			return WPFormsChallenge.admin.saveChallengeOption( optionData );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Resume Challenge and execute the callback.
 | |
| 		 *
 | |
| 		 * @since 1.7.5
 | |
| 		 *
 | |
| 		 * @param {object}   e        Event object.
 | |
| 		 * @param {Function} callback Callback function.
 | |
| 		 */
 | |
| 		resumeChallengeAndExec: function( e, callback ) {
 | |
| 
 | |
| 			if ( typeof callback !== 'function' ) {
 | |
| 				callback = function() {};
 | |
| 			}
 | |
| 
 | |
| 			if ( wpforms_challenge_admin.option.status !== 'paused' ) {
 | |
| 				callback();
 | |
| 
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			var resumeResult = app.resumeChallenge( e );
 | |
| 
 | |
| 			if ( typeof resumeResult === 'object' && typeof resumeResult.done === 'function' ) {
 | |
| 				resumeResult.done( callback );
 | |
| 			} else {
 | |
| 				callback();
 | |
| 			}
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Freeze/Unfreeze Challenge.
 | |
| 		 *
 | |
| 		 * @since 1.9.3
 | |
| 		 *
 | |
| 		 * @param {boolean} freeze      True to freeze, false to unfreeze.
 | |
| 		 * @param {string}  tooltipText Tooltip text.
 | |
| 		 */
 | |
| 		async freezeChallenge( freeze = true, tooltipText = '' ) {
 | |
| 			// Freeze the Challenge.
 | |
| 			if ( freeze ) {
 | |
| 				const closed = el.$listBlock.hasClass( 'closed' );
 | |
| 
 | |
| 				el.$challenge.addClass( 'frozen' ).data( 'was-closed', closed );
 | |
| 				el.$listBlock.addClass( 'closed' ).find( 'p' ).hide();
 | |
| 				app.initFrozenTooltip( tooltipText.length ? tooltipText : wpforms_challenge_admin.frozen_tooltip );
 | |
| 				app.pauseResumeChallenge( 'pause' );
 | |
| 
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			// Do not unfreeze if it's not frozen.
 | |
| 			if ( ! el.$challenge.hasClass( 'frozen' ) ) {
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			// Unfreeze the Challenge.
 | |
| 			el.$challenge.removeClass( 'frozen' );
 | |
| 			el.$progressBar.tooltipster( 'close' );
 | |
| 			app.pauseResumeChallenge( 'resume' );
 | |
| 
 | |
| 			// Restore the opened state.
 | |
| 			if ( ! el.$challenge.data( 'was-closed' ) ) {
 | |
| 				el.$listBlock.removeClass( 'closed' ).find( 'p' ).show();
 | |
| 			}
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Init the frozen Challenge tooltip.
 | |
| 		 *
 | |
| 		 * @since 1.9.3
 | |
| 		 *
 | |
| 		 * @param {string} tooltipText Tooltip text.
 | |
| 		 */
 | |
| 		initFrozenTooltip( tooltipText ) {
 | |
| 			let instance = el.$progressBar.data( 'tooltipster' );
 | |
| 
 | |
| 			if ( ! instance ) {
 | |
| 				const args = {
 | |
| 					content: '',
 | |
| 					trigger: 'manual',
 | |
| 					interactive: false,
 | |
| 					animationDuration: 100,
 | |
| 					maxWidth: 230,
 | |
| 					delay: 0,
 | |
| 					distance: 36,
 | |
| 					side: [ 'top' ],
 | |
| 					theme: [ 'tooltipster-default', 'wpforms-challenge-frozen-tooltip' ],
 | |
| 					contentAsHTML: true,
 | |
| 				};
 | |
| 
 | |
| 				// Initialize.
 | |
| 				el.$progressBar.tooltipster( args );
 | |
| 				instance = el.$progressBar.tooltipster( 'instance' );
 | |
| 				el.$progressBar.data( 'tooltipster', instance );
 | |
| 			}
 | |
| 
 | |
| 			el.$challenge.show();
 | |
| 			instance.content( tooltipText );
 | |
| 			instance.open();
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Refresh Page in order to re-init current step.
 | |
| 		 *
 | |
| 		 * @since 1.6.2
 | |
| 		 *
 | |
| 		 * @param {object} e Event object.
 | |
| 		 */
 | |
| 		refreshPage: function( e ) {
 | |
| 
 | |
| 			window.location.reload( true );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Check if we're in Gutenberg editor.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 *
 | |
| 		 * @returns {boolean} Is Gutenberg or not.
 | |
| 		 */
 | |
| 		isGutenberg: function() {
 | |
| 
 | |
| 			return typeof wp !== 'undefined' && Object.prototype.hasOwnProperty.call( wp, 'blocks' );
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Trigger form embed page save potentially reloading it.
 | |
| 		 *
 | |
| 		 * @since 1.5.0
 | |
| 		 */
 | |
| 		triggerPageSave: function() {
 | |
| 
 | |
| 			if ( app.isGutenberg() ) {
 | |
| 				app.gutenbergPageSave();
 | |
| 
 | |
| 			} else {
 | |
| 				$( '#post #publish' ).trigger( 'click' );
 | |
| 			}
 | |
| 		},
 | |
| 
 | |
| 		/**
 | |
| 		 * Save page for Gutenberg.
 | |
| 		 *
 | |
| 		 * @since 1.5.2
 | |
| 		 */
 | |
| 		gutenbergPageSave: function() {
 | |
| 
 | |
| 			var $gb = $( '.block-editor' ),
 | |
| 				$updateBtn = $gb.find( '.editor-post-publish-button.editor-post-publish-button__button' );
 | |
| 
 | |
| 			// Trigger click on the Update button.
 | |
| 			if ( $updateBtn.length > 0 ) {
 | |
| 				$updateBtn.trigger( 'click' );
 | |
| 
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 			// Use MutationObserver to wait while Gutenberg create/display panel with Publish button.
 | |
| 			var obs = {
 | |
| 				targetNode  : $gb.find( '.edit-post-layout, .block-editor-editor-skeleton__publish > div' )[0],
 | |
| 				config      : {
 | |
| 					childList: true,
 | |
| 					attributes: true,
 | |
| 					subtree: true,
 | |
| 				},
 | |
| 			};
 | |
| 
 | |
| 			obs.callback = function( mutationsList, observer ) {
 | |
| 
 | |
| 				var $btn = $gb.find( '.editor-post-publish-button, .editor-post-publish-panel__header-publish-button .editor-post-publish-button__button' );
 | |
| 
 | |
| 				if ( $btn.length > 0 ) {
 | |
| 					$btn.trigger( 'click' );
 | |
| 					observer.disconnect();
 | |
| 				}
 | |
| 			};
 | |
| 
 | |
| 			obs.observer = new MutationObserver( obs.callback );
 | |
| 			obs.observer.observe( obs.targetNode, obs.config );
 | |
| 
 | |
| 			// Trigger click on the Publish button that opens the additional publishing panel.
 | |
| 			$gb.find( '.edit-post-toggle-publish-panel__button, .editor-post-publish-panel__toggle.editor-post-publish-button__button' )
 | |
| 				.trigger( 'click' );
 | |
| 		},
 | |
| 	};
 | |
| 
 | |
| 	// Provide access to public functions/properties.
 | |
| 	return app;
 | |
| 
 | |
| }( document, window, jQuery ) );
 | |
| 
 | |
| WPFormsChallenge.core.init();
 |