/** * Pricing Slider JavaScript * Handles interactive functionality for both single and multiple sliders. */ (function($) { 'use strict'; // Add basic CSS for notifications that will be created by JS function addBasicNotificationCSS() { if ($('#king-addons-pricing-slider-notification-styles').length) { return; // Already added } var css = ` .king-addons-slider-notice { display: block; padding: 10px 15px; margin: 10px 0; border-radius: 4px; font-size: 14px; line-height: 1.5; } .king-addons-slider-notice.success { color: #3c763d; background-color: #dff0d8; border: 1px solid #d6e9c6; } .king-addons-slider-notice.error { color: #a94442; background-color: #f2dede; border: 1px solid #ebccd1; } .king-addons-slider-view-cart { display: block; margin: 10px 0; padding: 8px 12px; text-align: center; text-decoration: none; color: #4e4e4e; background-color: #f7f7f7; border: 1px solid #ddd; border-radius: 4px; transition: all 0.3s ease; } .king-addons-slider-view-cart:hover { color: #23527c; background-color: #e6e6e6; border-color: #adadad; } `; $('').appendTo('head'); } // Execute once when script loads addBasicNotificationCSS(); // --- Shared Helper Functions --- (Moved here) /** * Calculates the price based on the value and formula settings. * @param {number} value The current slider value. * @param {object} formulaSettings Settings object containing formula type and parameters. * @returns {number} Calculated price. */ function calculatePrice(value, formulaSettings) { if (!formulaSettings) return value; // Return raw value if no settings var formulaType = formulaSettings.advanced_formula_types || formulaSettings.formula || 'linear'; // Linear formula (Default in free version) if (formulaType === 'linear') { var a = typeof formulaSettings.a !== 'undefined' ? parseFloat(formulaSettings.a) : 1; var b = typeof formulaSettings.b !== 'undefined' ? parseFloat(formulaSettings.b) : 0; return a * value + b; } // Pro version formulas... else if (formulaType === 'exponential') { var base = typeof formulaSettings.formula_exp_base !== 'undefined' ? parseFloat(formulaSettings.formula_exp_base) : 1.1; var multiplier = typeof formulaSettings.formula_exp_multiplier !== 'undefined' ? parseFloat(formulaSettings.formula_exp_multiplier) : 1; return multiplier * Math.pow(base, value); } else if (formulaType === 'logarithmic') { var baseLog = typeof formulaSettings.formula_log_base !== 'undefined' ? parseFloat(formulaSettings.formula_log_base) : 10; var multiplierLog = typeof formulaSettings.formula_log_multiplier !== 'undefined' ? parseFloat(formulaSettings.formula_log_multiplier) : 10; value = Math.max(1, value); // Logarithm is undefined for 0 or negative return multiplierLog * (Math.log(value) / Math.log(baseLog)); } else if (formulaType === 'power') { var exponent = typeof formulaSettings.formula_power_exponent !== 'undefined' ? parseFloat(formulaSettings.formula_power_exponent) : 2; var multiplierPower = typeof formulaSettings.formula_power_multiplier !== 'undefined' ? parseFloat(formulaSettings.formula_power_multiplier) : 1; return multiplierPower * Math.pow(value, exponent); } else if (formulaType === 'custom') { // Find the closest custom price point <= current value var customPrices = formulaSettings.custom_prices || []; var bestMatchPrice = value; // Fallback var closestLowerVal = -Infinity; customPrices.forEach(function(point) { var pointValue = parseFloat(point.value); var pointPrice = parseFloat(point.price); if (!isNaN(pointValue) && !isNaN(pointPrice) && pointValue <= value && pointValue > closestLowerVal) { closestLowerVal = pointValue; bestMatchPrice = pointPrice; } }); return bestMatchPrice; } // Fallback to linear if formula type is unknown or default linear params if only 'linear' is specified var defaultA = typeof formulaSettings.a !== 'undefined' ? parseFloat(formulaSettings.a) : 1; var defaultB = typeof formulaSettings.b !== 'undefined' ? parseFloat(formulaSettings.b) : 0; return defaultA * value + defaultB; } /** * Formats a price number into an HTML string with currency, prefix, suffix, etc. * @param {number} price The numerical price. * @param {object} priceFormatData Settings for formatting (currency_symbol, etc.). * @returns {string} HTML string for the price display. */ function formatPriceHTML(price, priceFormatData) { var decimals = typeof priceFormatData.decimals !== 'undefined' ? parseInt(priceFormatData.decimals) : 2; var formattedPrice = parseFloat(price).toFixed(decimals); var currencySymbol = priceFormatData.currency_symbol || ''; var pricePrefix = priceFormatData.price_prefix || ''; var priceSuffix = priceFormatData.price_suffix || ''; var period = priceFormatData.period || ''; var currencyPosition = priceFormatData.currency_position || 'before'; var price_html = ''; if (currencyPosition === 'before') { price_html += '' + currencySymbol + ''; } if (pricePrefix) { price_html += '' + pricePrefix + ''; } // Ensure the value itself has the specific class for targeting if needed later price_html += '' + formattedPrice + ''; if (priceSuffix) { price_html += '' + priceSuffix + ''; } if (currencyPosition === 'after') { price_html += '' + currencySymbol + ''; } if (period) { price_html += '' + period + ''; } return price_html; } /** * Updates the visual state (icon, class) of feature items based on the current value. * @param {number} value The current slider value. * @param {jQuery} featureItems jQuery object of all feature list items. * @param {Array} featuresDefinition Array of feature definition objects from settings. */ function updateFeatures(value, featureItems, featuresDefinition) { featureItems.each(function() { var feature = $(this); // Use min-value from data attribute if present, otherwise assume 0 var minValueFeature = parseInt(feature.data('min-value')) || 0; // Determine if the feature should be 'included' based on the slider value var isIncluded = value >= minValueFeature; var featureTextElement = feature.find('.king-addons-pricing-slider__feature-text'); var featureText = featureTextElement.length ? featureTextElement.text().trim() : ''; // Get text content for matching var featureData = null; // Find the matching feature definition from the settings array if (Array.isArray(featuresDefinition) && featureText) { for (var i = 0; i < featuresDefinition.length; i++) { // Match based on the feature text content if (featuresDefinition[i] && featuresDefinition[i].text && featuresDefinition[i].text.trim() === featureText) { featureData = featuresDefinition[i]; break; } } } // Update icon and class if we found matching data if (featureData) { var iconSpan = feature.find('span:first-child'); // Assuming icon is in the first span var iconIncluded = featureData.icon_included || 'fas fa-check'; // Default icons var iconExcluded = featureData.icon_excluded || 'fas fa-times'; iconSpan.removeClass('king-addons-pricing-slider__feature-included king-addons-pricing-slider__feature-excluded'); if (isIncluded) { iconSpan.addClass('king-addons-pricing-slider__feature-included'); iconSpan.html(''); } else { iconSpan.addClass('king-addons-pricing-slider__feature-excluded'); iconSpan.html(''); } } else { // Optional: Handle case where feature item in HTML doesn't match any definition // console.warn('[KA Slider] No feature definition found for:', featureText); } }); } /** * Updates the UI elements of a single slider instance (progress bar, thumb, value display). * Moved here to be a shared helper. * @param {jQuery} singleSliderWrapper The wrapper (.king-addons-single-slider or .king-addons-pricing-slider). * @param {number} value The current value. */ function sharedUpdateSliderUI(singleSliderWrapper, value) { var rangeInput = singleSliderWrapper.find('.king-addons-pricing-slider__range'); var progressBar = singleSliderWrapper.find('.king-addons-pricing-slider__progress'); var customThumb = singleSliderWrapper.find('.king-addons-pricing-slider__custom-thumb'); var currentValDisplay = singleSliderWrapper.find('.king-addons-pricing-slider__current-value'); if (!rangeInput.length) { console.error('[KA Slider] Range input not found within:', singleSliderWrapper[0]); return; } if (!progressBar.length) { console.warn('[KA Slider] Progress bar not found within:', singleSliderWrapper[0]); // Continue without progress bar update } if (!customThumb.length) { console.warn('[KA Slider] Custom thumb not found within:', singleSliderWrapper[0]); // Log raw DOM element // Continue without thumb update } var min = parseFloat(rangeInput.attr('min')) || 0; var max = parseFloat(rangeInput.attr('max')) || 100; var percentage = 0; if (max > min) { percentage = ((value - min) / (max - min)) * 100; } else { // If max <= min, slider is effectively fixed. Percentage is 0 if value <= min, 100 if value >= max. // Or just treat as 0 to be safe. percentage = (value >= max) ? 100 : 0; console.warn('[KA Slider] Max <= Min for slider:', rangeInput[0]); } percentage = Math.max(0, Math.min(100, percentage)); // Clamp percentage // console.log('[KA Slider] sharedUpdateSliderUI called:', { // Keep logs for now // wrapper: singleSliderWrapper[0], // value: value, // percentage: percentage, // thumbElement: customThumb.length ? customThumb[0] : 'Not Found' // }); // Apply styles if (progressBar.length) { progressBar.css('width', percentage + '%'); } if (customThumb.length) { customThumb.css('left', percentage + '%'); // console.log('[KA Slider] Applied left: ' + percentage + '% to thumb:', customThumb[0]); } // Update current value indicator (if exists) if (currentValDisplay.length) { currentValDisplay.text(value); currentValDisplay.css('left', percentage + '%'); // Recalculate margin based on actual thumb height at the time of update var thumbHeight = customThumb.length ? customThumb.outerHeight() : 0; if (thumbHeight) { var indicatorMarginTop = (thumbHeight / 2) + 10; // 10px gap above thumb center currentValDisplay.css('margin-top', indicatorMarginTop + 'px'); } else { // Fallback margin if thumb isn't found or has no height currentValDisplay.css('margin-top', '22px'); } } } // --- Initialization Functions (Using Shared Helpers) --- /** * Initialize a specific SINGLE pricing slider. (Revisited Version) * @param {jQuery} sliderWrapper The main wrapper element (.king-addons-pricing-slider). */ function initSinglePricingSliderRevisited(sliderWrapper) { // Get elements var rangeInput = sliderWrapper.find('.king-addons-pricing-slider__range'); var priceWrapper = sliderWrapper.find('.king-addons-pricing-slider__price'); var button = sliderWrapper.find('.king-addons-pricing-slider__button'); // Could be or