/**
* 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