| @@ -1,0 +1,869 @@ | ||
| 1 | +/** | |
| 2 | + * Pricing Slider JavaScript | |
| 3 | + * Handles interactive functionality for both single and multiple sliders. | |
| 4 | + */ | |
| 5 | +(function($) { | |
| 6 | + 'use strict'; | |
| 7 | + | |
| 8 | + // Add basic CSS for notifications that will be created by JS | |
| 9 | + function addBasicNotificationCSS() { | |
| 10 | + if ($('#king-addons-pricing-slider-notification-styles').length) { | |
| 11 | + return; // Already added | |
| 12 | + } | |
| 13 | + | |
| 14 | + var css = ` | |
| 15 | + .king-addons-slider-notice { | |
| 16 | + display: block; | |
| 17 | + padding: 10px 15px; | |
| 18 | + margin: 10px 0; | |
| 19 | + border-radius: 4px; | |
| 20 | + font-size: 14px; | |
| 21 | + line-height: 1.5; | |
| 22 | + } | |
| 23 | + .king-addons-slider-notice.success { | |
| 24 | + color: #3c763d; | |
| 25 | + background-color: #dff0d8; | |
| 26 | + border: 1px solid #d6e9c6; | |
| 27 | + } | |
| 28 | + .king-addons-slider-notice.error { | |
| 29 | + color: #a94442; | |
| 30 | + background-color: #f2dede; | |
| 31 | + border: 1px solid #ebccd1; | |
| 32 | + } | |
| 33 | + .king-addons-slider-view-cart { | |
| 34 | + display: block; | |
| 35 | + margin: 10px 0; | |
| 36 | + padding: 8px 12px; | |
| 37 | + text-align: center; | |
| 38 | + text-decoration: none; | |
| 39 | + color: #4e4e4e; | |
| 40 | + background-color: #f7f7f7; | |
| 41 | + border: 1px solid #ddd; | |
| 42 | + border-radius: 4px; | |
| 43 | + transition: all 0.3s ease; | |
| 44 | + } | |
| 45 | + .king-addons-slider-view-cart:hover { | |
| 46 | + color: #23527c; | |
| 47 | + background-color: #e6e6e6; | |
| 48 | + border-color: #adadad; | |
| 49 | + } | |
| 50 | + `; | |
| 51 | + | |
| 52 | + $('<style id="king-addons-pricing-slider-notification-styles">' + css + '</style>').appendTo('head'); | |
| 53 | + } | |
| 54 | + | |
| 55 | + // Execute once when script loads | |
| 56 | + addBasicNotificationCSS(); | |
| 57 | + | |
| 58 | + // --- Shared Helper Functions --- (Moved here) | |
| 59 | + | |
| 60 | + /** | |
| 61 | + * Calculates the price based on the value and formula settings. | |
| 62 | + * @param {number} value The current slider value. | |
| 63 | + * @param {object} formulaSettings Settings object containing formula type and parameters. | |
| 64 | + * @returns {number} Calculated price. | |
| 65 | + */ | |
| 66 | + function calculatePrice(value, formulaSettings) { | |
| 67 | + if (!formulaSettings) return value; // Return raw value if no settings | |
| 68 | + | |
| 69 | + var formulaType = formulaSettings.advanced_formula_types || formulaSettings.formula || 'linear'; | |
| 70 | + | |
| 71 | + // Linear formula (Default in free version) | |
| 72 | + if (formulaType === 'linear') { | |
| 73 | + var a = typeof formulaSettings.a !== 'undefined' ? parseFloat(formulaSettings.a) : 1; | |
| 74 | + var b = typeof formulaSettings.b !== 'undefined' ? parseFloat(formulaSettings.b) : 0; | |
| 75 | + return a * value + b; | |
| 76 | + } | |
| 77 | + // Pro version formulas... | |
| 78 | + else if (formulaType === 'exponential') { | |
| 79 | + var base = typeof formulaSettings.formula_exp_base !== 'undefined' ? parseFloat(formulaSettings.formula_exp_base) : 1.1; | |
| 80 | + var multiplier = typeof formulaSettings.formula_exp_multiplier !== 'undefined' ? parseFloat(formulaSettings.formula_exp_multiplier) : 1; | |
| 81 | + return multiplier * Math.pow(base, value); | |
| 82 | + } | |
| 83 | + else if (formulaType === 'logarithmic') { | |
| 84 | + var baseLog = typeof formulaSettings.formula_log_base !== 'undefined' ? parseFloat(formulaSettings.formula_log_base) : 10; | |
| 85 | + var multiplierLog = typeof formulaSettings.formula_log_multiplier !== 'undefined' ? parseFloat(formulaSettings.formula_log_multiplier) : 10; | |
| 86 | + value = Math.max(1, value); // Logarithm is undefined for 0 or negative | |
| 87 | + return multiplierLog * (Math.log(value) / Math.log(baseLog)); | |
| 88 | + } | |
| 89 | + else if (formulaType === 'power') { | |
| 90 | + var exponent = typeof formulaSettings.formula_power_exponent !== 'undefined' ? parseFloat(formulaSettings.formula_power_exponent) : 2; | |
| 91 | + var multiplierPower = typeof formulaSettings.formula_power_multiplier !== 'undefined' ? parseFloat(formulaSettings.formula_power_multiplier) : 1; | |
| 92 | + return multiplierPower * Math.pow(value, exponent); | |
| 93 | + } | |
| 94 | + else if (formulaType === 'custom') { | |
| 95 | + // Find the closest custom price point <= current value | |
| 96 | + var customPrices = formulaSettings.custom_prices || []; | |
| 97 | + var bestMatchPrice = value; // Fallback | |
| 98 | + var closestLowerVal = -Infinity; | |
| 99 | + | |
| 100 | + customPrices.forEach(function(point) { | |
| 101 | + var pointValue = parseFloat(point.value); | |
| 102 | + var pointPrice = parseFloat(point.price); | |
| 103 | + if (!isNaN(pointValue) && !isNaN(pointPrice) && pointValue <= value && pointValue > closestLowerVal) { | |
| 104 | + closestLowerVal = pointValue; | |
| 105 | + bestMatchPrice = pointPrice; | |
| 106 | + } | |
| 107 | + }); | |
| 108 | + return bestMatchPrice; | |
| 109 | + } | |
| 110 | + | |
| 111 | + // Fallback to linear if formula type is unknown or default linear params if only 'linear' is specified | |
| 112 | + var defaultA = typeof formulaSettings.a !== 'undefined' ? parseFloat(formulaSettings.a) : 1; | |
| 113 | + var defaultB = typeof formulaSettings.b !== 'undefined' ? parseFloat(formulaSettings.b) : 0; | |
| 114 | + return defaultA * value + defaultB; | |
| 115 | + } | |
| 116 | + | |
| 117 | + /** | |
| 118 | + * Formats a price number into an HTML string with currency, prefix, suffix, etc. | |
| 119 | + * @param {number} price The numerical price. | |
| 120 | + * @param {object} priceFormatData Settings for formatting (currency_symbol, etc.). | |
| 121 | + * @returns {string} HTML string for the price display. | |
| 122 | + */ | |
| 123 | + function formatPriceHTML(price, priceFormatData) { | |
| 124 | + var decimals = typeof priceFormatData.decimals !== 'undefined' ? parseInt(priceFormatData.decimals) : 2; | |
| 125 | + var formattedPrice = parseFloat(price).toFixed(decimals); | |
| 126 | + var currencySymbol = priceFormatData.currency_symbol || ''; | |
| 127 | + var pricePrefix = priceFormatData.price_prefix || ''; | |
| 128 | + var priceSuffix = priceFormatData.price_suffix || ''; | |
| 129 | + var period = priceFormatData.period || ''; | |
| 130 | + var currencyPosition = priceFormatData.currency_position || 'before'; | |
| 131 | + | |
| 132 | + var price_html = ''; | |
| 133 | + if (currencyPosition === 'before') { | |
| 134 | + price_html += '<span class="king-addons-pricing-slider__currency">' + currencySymbol + '</span>'; | |
| 135 | + } | |
| 136 | + if (pricePrefix) { | |
| 137 | + price_html += '<span class="king-addons-pricing-slider__prefix">' + pricePrefix + '</span>'; | |
| 138 | + } | |
| 139 | + // Ensure the value itself has the specific class for targeting if needed later | |
| 140 | + price_html += '<span class="king-addons-pricing-slider__price-value">' + formattedPrice + '</span>'; | |
| 141 | + if (priceSuffix) { | |
| 142 | + price_html += '<span class="king-addons-pricing-slider__suffix">' + priceSuffix + '</span>'; | |
| 143 | + } | |
| 144 | + if (currencyPosition === 'after') { | |
| 145 | + price_html += '<span class="king-addons-pricing-slider__currency">' + currencySymbol + '</span>'; | |
| 146 | + } | |
| 147 | + if (period) { | |
| 148 | + price_html += '<span class="king-addons-pricing-slider__period">' + period + '</span>'; | |
| 149 | + } | |
| 150 | + return price_html; | |
| 151 | + } | |
| 152 | + | |
| 153 | + /** | |
| 154 | + * Updates the visual state (icon, class) of feature items based on the current value. | |
| 155 | + * @param {number} value The current slider value. | |
| 156 | + * @param {jQuery} featureItems jQuery object of all feature list items. | |
| 157 | + * @param {Array} featuresDefinition Array of feature definition objects from settings. | |
| 158 | + */ | |
| 159 | + function updateFeatures(value, featureItems, featuresDefinition) { | |
| 160 | + featureItems.each(function() { | |
| 161 | + var feature = $(this); | |
| 162 | + // Use min-value from data attribute if present, otherwise assume 0 | |
| 163 | + var minValueFeature = parseInt(feature.data('min-value')) || 0; | |
| 164 | + // Determine if the feature should be 'included' based on the slider value | |
| 165 | + var isIncluded = value >= minValueFeature; | |
| 166 | + | |
| 167 | + var featureTextElement = feature.find('.king-addons-pricing-slider__feature-text'); | |
| 168 | + var featureText = featureTextElement.length ? featureTextElement.text().trim() : ''; // Get text content for matching | |
| 169 | + var featureData = null; | |
| 170 | + | |
| 171 | + // Find the matching feature definition from the settings array | |
| 172 | + if (Array.isArray(featuresDefinition) && featureText) { | |
| 173 | + for (var i = 0; i < featuresDefinition.length; i++) { | |
| 174 | + // Match based on the feature text content | |
| 175 | + if (featuresDefinition[i] && featuresDefinition[i].text && featuresDefinition[i].text.trim() === featureText) { | |
| 176 | + featureData = featuresDefinition[i]; | |
| 177 | + break; | |
| 178 | + } | |
| 179 | + } | |
| 180 | + } | |
| 181 | + | |
| 182 | + // Update icon and class if we found matching data | |
| 183 | + if (featureData) { | |
| 184 | + var iconSpan = feature.find('span:first-child'); // Assuming icon is in the first span | |
| 185 | + var iconIncluded = featureData.icon_included || 'fas fa-check'; // Default icons | |
| 186 | + var iconExcluded = featureData.icon_excluded || 'fas fa-times'; | |
| 187 | + | |
| 188 | + iconSpan.removeClass('king-addons-pricing-slider__feature-included king-addons-pricing-slider__feature-excluded'); | |
| 189 | + | |
| 190 | + if (isIncluded) { | |
| 191 | + iconSpan.addClass('king-addons-pricing-slider__feature-included'); | |
| 192 | + iconSpan.html('<i class="' + iconIncluded + '" aria-hidden="true"></i>'); | |
| 193 | + } else { | |
| 194 | + iconSpan.addClass('king-addons-pricing-slider__feature-excluded'); | |
| 195 | + iconSpan.html('<i class="' + iconExcluded + '" aria-hidden="true"></i>'); | |
| 196 | + } | |
| 197 | + } else { | |
| 198 | + // Optional: Handle case where feature item in HTML doesn't match any definition | |
| 199 | + // console.warn('[KA Slider] No feature definition found for:', featureText); | |
| 200 | + } | |
| 201 | + }); | |
| 202 | + } | |
| 203 | + | |
| 204 | + /** | |
| 205 | + * Updates the UI elements of a single slider instance (progress bar, thumb, value display). | |
| 206 | + * Moved here to be a shared helper. | |
| 207 | + * @param {jQuery} singleSliderWrapper The wrapper (.king-addons-single-slider or .king-addons-pricing-slider). | |
| 208 | + * @param {number} value The current value. | |
| 209 | + */ | |
| 210 | + function sharedUpdateSliderUI(singleSliderWrapper, value) { | |
| 211 | + var rangeInput = singleSliderWrapper.find('.king-addons-pricing-slider__range'); | |
| 212 | + var progressBar = singleSliderWrapper.find('.king-addons-pricing-slider__progress'); | |
| 213 | + var customThumb = singleSliderWrapper.find('.king-addons-pricing-slider__custom-thumb'); | |
| 214 | + var currentValDisplay = singleSliderWrapper.find('.king-addons-pricing-slider__current-value'); | |
| 215 | + | |
| 216 | + if (!rangeInput.length) { | |
| 217 | + // console.error('[KA Slider] Range input not found within:', singleSliderWrapper[0]); | |
| 218 | + return; | |
| 219 | + } | |
| 220 | + if (!progressBar.length) { | |
| 221 | + // console.warn('[KA Slider] Progress bar not found within:', singleSliderWrapper[0]); | |
| 222 | + // Continue without progress bar update | |
| 223 | + } | |
| 224 | + if (!customThumb.length) { | |
| 225 | + // console.warn('[KA Slider] Custom thumb not found within:', singleSliderWrapper[0]); // Log raw DOM element | |
| 226 | + // Continue without thumb update | |
| 227 | + } | |
| 228 | + | |
| 229 | + var min = parseFloat(rangeInput.attr('min')) || 0; | |
| 230 | + var max = parseFloat(rangeInput.attr('max')) || 100; | |
| 231 | + var percentage = 0; | |
| 232 | + | |
| 233 | + if (max > min) { | |
| 234 | + percentage = ((value - min) / (max - min)) * 100; | |
| 235 | + } else { | |
| 236 | + // If max <= min, slider is effectively fixed. Percentage is 0 if value <= min, 100 if value >= max. | |
| 237 | + // Or just treat as 0 to be safe. | |
| 238 | + percentage = (value >= max) ? 100 : 0; | |
| 239 | + // console.warn('[KA Slider] Max <= Min for slider:', rangeInput[0]); | |
| 240 | + } | |
| 241 | + | |
| 242 | + percentage = Math.max(0, Math.min(100, percentage)); // Clamp percentage | |
| 243 | + | |
| 244 | + // console.log('[KA Slider] sharedUpdateSliderUI called:', { // Keep logs for now | |
| 245 | + // wrapper: singleSliderWrapper[0], | |
| 246 | + // value: value, | |
| 247 | + // percentage: percentage, | |
| 248 | + // thumbElement: customThumb.length ? customThumb[0] : 'Not Found' | |
| 249 | + // }); | |
| 250 | + | |
| 251 | + // Apply styles | |
| 252 | + if (progressBar.length) { | |
| 253 | + progressBar.css('width', percentage + '%'); | |
| 254 | + } | |
| 255 | + if (customThumb.length) { | |
| 256 | + customThumb.css('left', percentage + '%'); | |
| 257 | + // console.log('[KA Slider] Applied left: ' + percentage + '% to thumb:', customThumb[0]); | |
| 258 | + } | |
| 259 | + | |
| 260 | + // Update current value indicator (if exists) | |
| 261 | + if (currentValDisplay.length) { | |
| 262 | + currentValDisplay.text(value); | |
| 263 | + currentValDisplay.css('left', percentage + '%'); | |
| 264 | + // Recalculate margin based on actual thumb height at the time of update | |
| 265 | + var thumbHeight = customThumb.length ? customThumb.outerHeight() : 0; | |
| 266 | + if (thumbHeight) { | |
| 267 | + var indicatorMarginTop = (thumbHeight / 2) + 10; // 10px gap above thumb center | |
| 268 | + currentValDisplay.css('margin-top', indicatorMarginTop + 'px'); | |
| 269 | + } else { | |
| 270 | + // Fallback margin if thumb isn't found or has no height | |
| 271 | + currentValDisplay.css('margin-top', '22px'); | |
| 272 | + } | |
| 273 | + } | |
| 274 | + } | |
| 275 | + | |
| 276 | + // --- Initialization Functions (Using Shared Helpers) --- | |
| 277 | + | |
| 278 | + /** | |
| 279 | + * Initialize a specific SINGLE pricing slider. (Revisited Version) | |
| 280 | + * @param {jQuery} sliderWrapper The main wrapper element (.king-addons-pricing-slider). | |
| 281 | + */ | |
| 282 | + function initSinglePricingSliderRevisited(sliderWrapper) { | |
| 283 | + // Get elements | |
| 284 | + var rangeInput = sliderWrapper.find('.king-addons-pricing-slider__range'); | |
| 285 | + var priceWrapper = sliderWrapper.find('.king-addons-pricing-slider__price'); | |
| 286 | + var button = sliderWrapper.find('.king-addons-pricing-slider__button'); // Could be <a> or <button> | |
| 287 | + var features = sliderWrapper.find('.king-addons-pricing-slider__feature-item'); | |
| 288 | + | |
| 289 | + if (!rangeInput.length) { | |
| 290 | + // console.error("[KA Slider] Single slider init failed: Range input not found in", sliderWrapper[0]); | |
| 291 | + return; | |
| 292 | + } | |
| 293 | + | |
| 294 | + // Get data (use attributes as fallback for robustness) | |
| 295 | + var defaultValue = parseFloat(rangeInput.val()) || parseFloat(sliderWrapper.data('default-value')) || parseFloat(rangeInput.attr('min')) || 0; | |
| 296 | + var priceData = sliderWrapper.data('price-data') || {}; // Get combined price settings | |
| 297 | + var featuresData = sliderWrapper.data('features') || []; // Get features definitions | |
| 298 | + | |
| 299 | + // Correct initial value if it's outside min/max bounds | |
| 300 | + var minValue = parseFloat(rangeInput.attr('min')) || 0; | |
| 301 | + var maxValue = parseFloat(rangeInput.attr('max')) || 100; | |
| 302 | + defaultValue = Math.max(minValue, Math.min(maxValue, defaultValue)); | |
| 303 | + if (parseFloat(rangeInput.val()) !== defaultValue) { | |
| 304 | + rangeInput.val(defaultValue); // Ensure input value matches calculated default | |
| 305 | + } | |
| 306 | + | |
| 307 | + /** | |
| 308 | + * Updates the price display, button link (if applicable), and features for the single slider. | |
| 309 | + */ | |
| 310 | + function updateDisplay() { | |
| 311 | + var value = parseFloat(rangeInput.val()); // Get current value from input | |
| 312 | + var price = calculatePrice(value, priceData); // Use shared function | |
| 313 | + var new_price_html = formatPriceHTML(price, priceData); // Use shared function | |
| 314 | + | |
| 315 | + if (priceWrapper.length) { | |
| 316 | + priceWrapper.html(new_price_html); | |
| 317 | + } | |
| 318 | + | |
| 319 | + // Update button URL only if it's a link (<a> tag) | |
| 320 | + if (button.length && button.is('a')) { | |
| 321 | + var url = button.attr('href'); | |
| 322 | + // Check if href exists and is not just '#' or empty | |
| 323 | + if (url && url !== '#') { | |
| 324 | + try { | |
| 325 | + var urlParts = url.split('?'); | |
| 326 | + var baseUrl = urlParts[0]; | |
| 327 | + var params = new URLSearchParams(urlParts.length > 1 ? urlParts[1] : ''); | |
| 328 | + params.set('price', parseFloat(price).toFixed(priceData.decimals || 2)); // Use decimals from data | |
| 329 | + button.attr('href', baseUrl + '?' + params.toString()); | |
| 330 | + } catch (e) { | |
| 331 | + // console.error("[KA Slider] Error updating button URL:", e); | |
| 332 | + // Potentially invalid URL, leave it as is | |
| 333 | + } | |
| 334 | + } | |
| 335 | + } | |
| 336 | + | |
| 337 | + updateFeatures(value, features, featuresData); // Update icons | |
| 338 | + | |
| 339 | + // --- Add dynamic class update for feature list items --- START | |
| 340 | + if (features.length) { | |
| 341 | + features.each(function() { | |
| 342 | + var item = $(this); | |
| 343 | + var minValue = parseFloat(item.data('min-value')); // Get minimum value from data-attribute | |
| 344 | + var isIncluded = value >= minValue; | |
| 345 | + | |
| 346 | + // Remove both classes before adding the correct one | |
| 347 | + item.removeClass('king-addons-feature-included king-addons-feature-excluded'); | |
| 348 | + | |
| 349 | + if (isIncluded) { | |
| 350 | + item.addClass('king-addons-feature-included'); | |
| 351 | + } else { | |
| 352 | + item.addClass('king-addons-feature-excluded'); | |
| 353 | + } | |
| 354 | + }); | |
| 355 | + } | |
| 356 | + // --- Add dynamic class update for feature list items --- END | |
| 357 | + } | |
| 358 | + | |
| 359 | + // --- Initialization --- | |
| 360 | + sharedUpdateSliderUI(sliderWrapper, defaultValue); // Initial UI positioning | |
| 361 | + updateDisplay(); // Initial price, features, button link update | |
| 362 | + | |
| 363 | + // --- Event Listener --- | |
| 364 | + // Ensure previous listeners are removed before adding new ones (important for editor) | |
| 365 | + rangeInput.off('.pricingSlider').on('input.pricingSlider change.pricingSlider', function() { | |
| 366 | + var value = parseFloat($(this).val()); | |
| 367 | + sharedUpdateSliderUI(sliderWrapper, value); // Update slider UI visuals | |
| 368 | + updateDisplay(); // Update price, button, features based on new value | |
| 369 | + }); | |
| 370 | + } | |
| 371 | + | |
| 372 | + /** | |
| 373 | + * Initialize MULTIPLE pricing sliders within a container. (Revisited Version) | |
| 374 | + * @param {jQuery} slidersContainer The main wrapper element (.king-addons-pricing-sliders). | |
| 375 | + */ | |
| 376 | + function initMultiplePricingSlidersRevisited(slidersContainer) { | |
| 377 | + // Get common elements and data for the whole group | |
| 378 | + var priceData = slidersContainer.data('price-data') || {}; // Combined price display settings | |
| 379 | + var featuresData = slidersContainer.data('features') || []; // Combined features definitions | |
| 380 | + var priceWrapper = slidersContainer.find('.king-addons-pricing-slider__price'); // The single price display for the group | |
| 381 | + var featuresList = slidersContainer.find('.king-addons-pricing-slider__feature-list'); // Container for features | |
| 382 | + var button = slidersContainer.find('.king-addons-pricing-slider__button'); // Combined button (link or WC) | |
| 383 | + var addToCartButton = slidersContainer.find('.king-addons-pricing-slider__add-to-cart'); // Specific WC button | |
| 384 | + var individualSliders = slidersContainer.find('.king-addons-single-slider'); // Wrappers for each slider | |
| 385 | + var allSlidersData = slidersContainer.data('sliders') || []; // Array of settings for each slider (from repeater) | |
| 386 | + | |
| 387 | + if (!individualSliders.length) { | |
| 388 | + // console.error("[KA Slider] Multi-slider init failed: No individual sliders found in", slidersContainer[0]); | |
| 389 | + return; | |
| 390 | + } | |
| 391 | + if (allSlidersData.length !== individualSliders.length) { | |
| 392 | + // console.warn("[KA Slider] Mismatch between slider data count and slider element count."); | |
| 393 | + // Attempt to continue, but calculations might be off | |
| 394 | + } | |
| 395 | + | |
| 396 | + /** | |
| 397 | + * Calculates the combined weighted value/price for all sliders in the group. | |
| 398 | + * @returns {number} The final calculated price. | |
| 399 | + */ | |
| 400 | + function calculateCombinedPriceForMulti() { | |
| 401 | + var combinedValue = 0; | |
| 402 | + var totalWeight = 0; | |
| 403 | + | |
| 404 | + individualSliders.each(function(index) { | |
| 405 | + var singleSliderWrapper = $(this); | |
| 406 | + var rangeInput = singleSliderWrapper.find('.king-addons-pricing-slider__range'); | |
| 407 | + if (!rangeInput.length) return; // Skip if input not found | |
| 408 | + | |
| 409 | + var value = parseFloat(rangeInput.val()); | |
| 410 | + // Get weight from the corresponding data object, default to 1 if missing or invalid | |
| 411 | + var weight = (allSlidersData[index] && typeof allSlidersData[index].weight !== 'undefined' && !isNaN(parseFloat(allSlidersData[index].weight))) | |
| 412 | + ? parseFloat(allSlidersData[index].weight) | |
| 413 | + : 1; | |
| 414 | + | |
| 415 | + combinedValue += value * weight; | |
| 416 | + totalWeight += weight; | |
| 417 | + }); | |
| 418 | + | |
| 419 | + // Avoid division by zero if totalWeight is 0 | |
| 420 | + var averageValue = (totalWeight > 0) ? (combinedValue / totalWeight) : 0; | |
| 421 | + | |
| 422 | + // Now calculate the final price based on the combined/averaged value using the group's price settings | |
| 423 | + return calculatePrice(averageValue, priceData); // Use shared function | |
| 424 | + } | |
| 425 | + | |
| 426 | + /** | |
| 427 | + * Updates the combined price display, features list, and button(s) for the group. | |
| 428 | + */ | |
| 429 | + function updateCombinedDisplayForMulti() { | |
| 430 | + var finalPrice = calculateCombinedPriceForMulti(); // Use the final calculated price/value for feature check | |
| 431 | + var new_price_html = formatPriceHTML(finalPrice, priceData); // Use shared function | |
| 432 | + | |
| 433 | + if (priceWrapper.length) { | |
| 434 | + priceWrapper.html(new_price_html); | |
| 435 | + } | |
| 436 | + | |
| 437 | + // Update features based on the final calculated price/value | |
| 438 | + if (featuresList.length) { | |
| 439 | + var featureItems = featuresList.find('.king-addons-pricing-slider__feature-item'); | |
| 440 | + updateFeatures(finalPrice, featureItems, featuresData); // Update icons based on final price | |
| 441 | + | |
| 442 | + // --- Add dynamic class update for feature list items (Multi-Slider) --- START | |
| 443 | + featureItems.each(function() { | |
| 444 | + var item = $(this); | |
| 445 | + var minValue = parseFloat(item.data('min-value')); | |
| 446 | + // Use the final calculated price/value to determine inclusion for the combined list | |
| 447 | + var isIncluded = finalPrice >= minValue; | |
| 448 | + | |
| 449 | + item.removeClass('king-addons-feature-included king-addons-feature-excluded'); | |
| 450 | + | |
| 451 | + if (isIncluded) { | |
| 452 | + item.addClass('king-addons-feature-included'); | |
| 453 | + } else { | |
| 454 | + item.addClass('king-addons-feature-excluded'); | |
| 455 | + } | |
| 456 | + }); | |
| 457 | + // --- Add dynamic class update for feature list items (Multi-Slider) --- END | |
| 458 | + } | |
| 459 | + | |
| 460 | + // Update standard button link (if it's an <a> tag) | |
| 461 | + if (button.length && button.is('a')) { | |
| 462 | + var url = button.attr('href'); | |
| 463 | + if (url && url !== '#') { | |
| 464 | + try { | |
| 465 | + var urlParts = url.split('?'); | |
| 466 | + var baseUrl = urlParts[0]; | |
| 467 | + var params = new URLSearchParams(urlParts.length > 1 ? urlParts[1] : ''); | |
| 468 | + params.set('price', parseFloat(finalPrice).toFixed(priceData.decimals || 2)); | |
| 469 | + button.attr('href', baseUrl + '?' + params.toString()); | |
| 470 | + } catch(e) { | |
| 471 | + // console.error("[KA Slider] Error updating multi-slider button URL:", e); | |
| 472 | + } | |
| 473 | + } | |
| 474 | + } | |
| 475 | + // Update WooCommerce button data attribute (if exists) | |
| 476 | + if (addToCartButton.length) { | |
| 477 | + addToCartButton.data('price', finalPrice); // Store calculated price for potential AJAX use | |
| 478 | + addToCartButton.attr('data-price', finalPrice); // Also set attribute for easier debugging/selection | |
| 479 | + } | |
| 480 | + } | |
| 481 | + | |
| 482 | + // --- Initialization --- | |
| 483 | + // Initialize UI for each individual slider | |
| 484 | + individualSliders.each(function(index) { | |
| 485 | + var singleSliderWrapper = $(this); | |
| 486 | + var rangeInput = singleSliderWrapper.find('.king-addons-pricing-slider__range'); | |
| 487 | + if (!rangeInput.length) return; // Skip if no input | |
| 488 | + | |
| 489 | + var minValue = parseFloat(rangeInput.attr('min')) || 0; | |
| 490 | + var maxValue = parseFloat(rangeInput.attr('max')) || 100; | |
| 491 | + // Use default value from data if available, otherwise from input, clamped | |
| 492 | + var defaultValue = (allSlidersData[index] && typeof allSlidersData[index].default_value !== 'undefined') | |
| 493 | + ? parseFloat(allSlidersData[index].default_value) | |
| 494 | + : parseFloat(rangeInput.val()); | |
| 495 | + defaultValue = Math.max(minValue, Math.min(maxValue, defaultValue || minValue)); | |
| 496 | + | |
| 497 | + if (parseFloat(rangeInput.val()) !== defaultValue) { | |
| 498 | + rangeInput.val(defaultValue); // Set initial value correctly | |
| 499 | + } | |
| 500 | + sharedUpdateSliderUI(singleSliderWrapper, defaultValue); // Use shared function | |
| 501 | + }); | |
| 502 | + // Update the combined display based on initial values | |
| 503 | + updateCombinedDisplayForMulti(); | |
| 504 | + | |
| 505 | + // --- Event Listener (Delegated) --- | |
| 506 | + // Remove previous listeners first | |
| 507 | + slidersContainer.off('.pricingSlider', '.king-addons-pricing-slider__range'); | |
| 508 | + // Add new delegated listener | |
| 509 | + slidersContainer.on('input.pricingSlider change.pricingSlider', '.king-addons-pricing-slider__range', function() { | |
| 510 | + var rangeInput = $(this); // The input that triggered the event | |
| 511 | + var singleSliderWrapper = rangeInput.closest('.king-addons-single-slider'); | |
| 512 | + var value = parseFloat(rangeInput.val()); | |
| 513 | + | |
| 514 | + // Log for debugging multi-slider events | |
| 515 | + // console.log('[KA Slider] Multi-Slider Input Event Fired:', { | |
| 516 | + // value: value, | |
| 517 | + // targetSliderWrapper: singleSliderWrapper.length ? singleSliderWrapper[0] : 'Not Found', | |
| 518 | + // eventTarget: rangeInput[0] | |
| 519 | + // }); | |
| 520 | + | |
| 521 | + if (!singleSliderWrapper.length) { | |
| 522 | + // console.error('[KA Slider] Could not find .king-addons-single-slider parent for', rangeInput[0]); | |
| 523 | + return; | |
| 524 | + } | |
| 525 | + | |
| 526 | + sharedUpdateSliderUI(singleSliderWrapper, value); // Update the UI of the slider that changed | |
| 527 | + updateCombinedDisplayForMulti(); // Recalculate and update the combined price/features | |
| 528 | + }); | |
| 529 | + | |
| 530 | + // --- WooCommerce Handler Placeholder --- | |
| 531 | + if (addToCartButton.length) { | |
| 532 | + addToCartButton.off('click.wcAddToCart').on('click.wcAddToCart', function(e) { | |
| 533 | + e.preventDefault(); | |
| 534 | + var $button = $(this); | |
| 535 | + | |
| 536 | + // --- DEBUGGING START --- | |
| 537 | + // console.log('[KA Slider] Add to Cart clicked. Localized vars:', typeof king_addons_slider_vars !== 'undefined' ? king_addons_slider_vars : 'Not Defined'); | |
| 538 | + // --- DEBUGGING END --- | |
| 539 | + | |
| 540 | + var price = $button.data('price') || calculateCombinedPriceForMulti(); // Get price | |
| 541 | + var productId = $button.data('product-id'); | |
| 542 | + var productType = $button.data('product-type') || 'dynamic'; // Get product type | |
| 543 | + var autoQuantity = $button.data('auto-quantity') === 'yes'; // Check if auto quantity is enabled | |
| 544 | + var useAsBudget = $button.data('use-budget') === 'yes'; // Check if using slider as budget | |
| 545 | + | |
| 546 | + var quantity = 1; // Default quantity | |
| 547 | + | |
| 548 | + // If auto quantity is enabled, try to get the value from the relevant slider | |
| 549 | + // NOTE: This assumes auto_quantity makes sense primarily with a single slider scenario. | |
| 550 | + // If used with multiple, logic might need refinement (e.g., use combined value?). | |
| 551 | + if (autoQuantity) { | |
| 552 | + // Find the slider input within the same container as the button | |
| 553 | + // This works for single slider and might need adjustment for complex multi-slider quantity logic | |
| 554 | + var $sliderInput = $button.closest('.king-addons-pricing-slider, .king-addons-pricing-sliders').find('.king-addons-pricing-slider__range').first(); // Get the first slider in the context | |
| 555 | + if ($sliderInput.length) { | |
| 556 | + quantity = parseInt($sliderInput.val()) || 1; | |
| 557 | + } else { | |
| 558 | + // console.warn('[KA Slider] Could not find slider input for auto quantity.'); | |
| 559 | + } | |
| 560 | + quantity = Math.max(1, quantity); // Ensure quantity is at least 1 | |
| 561 | + } | |
| 562 | + | |
| 563 | + if (!productId && productType === 'specific') { // Only require product ID for specific type | |
| 564 | + // Maybe show a user-facing error? | |
| 565 | + // Clear any existing notices or links | |
| 566 | + $button.siblings('.king-addons-slider-notice, .king-addons-slider-view-cart').remove(); | |
| 567 | + var errorMsg = $('<span class="king-addons-slider-notice error">Missing Product ID.</span>').insertAfter($button); | |
| 568 | + setTimeout(function(){ errorMsg.fadeOut(function(){ $(this).remove(); }); }, 5000); | |
| 569 | + return; | |
| 570 | + } | |
| 571 | + | |
| 572 | + // Basic AJAX add-to-cart implementation (needs refinement) | |
| 573 | + // Consider adding loading state to button | |
| 574 | + $button.addClass('loading'); | |
| 575 | + // Clear any existing notices or links before making the request | |
| 576 | + $button.siblings('.king-addons-slider-notice, .king-addons-slider-view-cart').remove(); | |
| 577 | + | |
| 578 | + $.ajax({ | |
| 579 | + type: 'POST', | |
| 580 | + url: king_addons_slider_vars.ajax_url, // Make sure this variable is localized | |
| 581 | + data: { | |
| 582 | + action: 'king_addons_add_to_cart', // PHP action hook | |
| 583 | + nonce: $button.data('nonce'), // Get nonce from button data attribute | |
| 584 | + product_id: productId, | |
| 585 | + price: price, // Send the calculated price | |
| 586 | + product_type: productType, | |
| 587 | + quantity: quantity, | |
| 588 | + use_as_budget: useAsBudget ? 'true' : 'false', // Send as string 'true'/'false' | |
| 589 | + auto_quantity: autoQuantity ? 'true' : 'false' // Send as string 'true'/'false' | |
| 590 | + }, | |
| 591 | + success: function(response) { | |
| 592 | + $button.removeClass('loading'); | |
| 593 | + | |
| 594 | + // Find the target container for notifications based on settings | |
| 595 | + var notificationsContainer; | |
| 596 | + var notificationPosition = (priceData && priceData.notification_position) ? priceData.notification_position : 'after_button'; | |
| 597 | + | |
| 598 | + // Remove existing notifications | |
| 599 | + $button.closest('.king-addons-pricing-slider, .king-addons-pricing-sliders') | |
| 600 | + .find('.king-addons-slider-notice, .king-addons-slider-view-cart').remove(); | |
| 601 | + | |
| 602 | + switch(notificationPosition) { | |
| 603 | + case 'before_button': | |
| 604 | + notificationsContainer = $button.parent(); | |
| 605 | + // Insert before button | |
| 606 | + if (response.success) { | |
| 607 | + // console.log('[KA Slider] Product added to cart:', response.data); | |
| 608 | + // Trigger WooCommerce added_to_cart event for themes/plugins to hook into | |
| 609 | + $(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, $button]); | |
| 610 | + | |
| 611 | + var successMsg = $('<span class="king-addons-slider-notice success">' + (response.data.message || 'Added to cart!') + '</span>'); | |
| 612 | + successMsg.insertBefore($button); | |
| 613 | + | |
| 614 | + if (response.data.cart_url) { | |
| 615 | + var viewCartText = (typeof king_addons_slider_vars !== 'undefined' && king_addons_slider_vars.view_cart_text) | |
| 616 | + ? king_addons_slider_vars.view_cart_text : 'View Cart'; | |
| 617 | + var viewCartLink = $('<a href="' + response.data.cart_url + '" class="king-addons-slider-view-cart">' + viewCartText + '</a>'); | |
| 618 | + viewCartLink.insertBefore($button); | |
| 619 | + | |
| 620 | + setTimeout(function(){ | |
| 621 | + successMsg.fadeOut(function(){ $(this).remove(); }); | |
| 622 | + viewCartLink.fadeOut(function(){ $(this).remove(); }); | |
| 623 | + }, 5000); | |
| 624 | + } else { | |
| 625 | + setTimeout(function(){ successMsg.fadeOut(function(){ $(this).remove(); }); }, 3000); | |
| 626 | + } | |
| 627 | + } else { | |
| 628 | + var errorMessage = 'Could not add product to cart. Please try again.'; | |
| 629 | + if (response.data && response.data.error_code === 'budget_too_low') { | |
| 630 | + var templateMessage = priceData.budget_too_low_message || 'Your selected budget is too low. Please increase the budget to at least {product_price}.'; | |
| 631 | + var productPrice = response.data.product_price || ''; | |
| 632 | + errorMessage = templateMessage.replace('{product_price}', productPrice); | |
| 633 | + } else if (response.data && response.data.message) { | |
| 634 | + errorMessage = response.data.message; | |
| 635 | + } | |
| 636 | + | |
| 637 | + var errorMsg = $('<span class="king-addons-slider-notice error">' + errorMessage + '</span>'); | |
| 638 | + errorMsg.insertBefore($button); | |
| 639 | + setTimeout(function(){ errorMsg.fadeOut(function(){ $(this).remove(); }); }, 5000); | |
| 640 | + } | |
| 641 | + break; | |
| 642 | + | |
| 643 | + case 'after_price': | |
| 644 | + var priceElement = $button.closest('.king-addons-pricing-slider, .king-addons-pricing-sliders') | |
| 645 | + .find('.king-addons-pricing-slider__price'); | |
| 646 | + if (priceElement.length) { | |
| 647 | + if (response.success) { | |
| 648 | + // console.log('[KA Slider] Product added to cart:', response.data); | |
| 649 | + // Trigger WooCommerce added_to_cart event for themes/plugins to hook into | |
| 650 | + $(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, $button]); | |
| 651 | + | |
| 652 | + var successMsg = $('<span class="king-addons-slider-notice success">' + (response.data.message || 'Added to cart!') + '</span>'); | |
| 653 | + successMsg.insertAfter(priceElement); | |
| 654 | + | |
| 655 | + if (response.data.cart_url) { | |
| 656 | + var viewCartText = (typeof king_addons_slider_vars !== 'undefined' && king_addons_slider_vars.view_cart_text) | |
| 657 | + ? king_addons_slider_vars.view_cart_text : 'View Cart'; | |
| 658 | + var viewCartLink = $('<a href="' + response.data.cart_url + '" class="king-addons-slider-view-cart">' + viewCartText + '</a>'); | |
| 659 | + viewCartLink.insertAfter(successMsg); | |
| 660 | + | |
| 661 | + setTimeout(function(){ | |
| 662 | + successMsg.fadeOut(function(){ $(this).remove(); }); | |
| 663 | + viewCartLink.fadeOut(function(){ $(this).remove(); }); | |
| 664 | + }, 5000); | |
| 665 | + } else { | |
| 666 | + setTimeout(function(){ successMsg.fadeOut(function(){ $(this).remove(); }); }, 3000); | |
| 667 | + } | |
| 668 | + } else { | |
| 669 | + var errorMessage = 'Could not add product to cart. Please try again.'; | |
| 670 | + if (response.data && response.data.error_code === 'budget_too_low') { | |
| 671 | + var templateMessage = priceData.budget_too_low_message || 'Your selected budget is too low. Please increase the budget to at least {product_price}.'; | |
| 672 | + var productPrice = response.data.product_price || ''; | |
| 673 | + errorMessage = templateMessage.replace('{product_price}', productPrice); | |
| 674 | + } else if (response.data && response.data.message) { | |
| 675 | + errorMessage = response.data.message; | |
| 676 | + } | |
| 677 | + | |
| 678 | + var errorMsg = $('<span class="king-addons-slider-notice error">' + errorMessage + '</span>'); | |
| 679 | + errorMsg.insertAfter(priceElement); | |
| 680 | + setTimeout(function(){ errorMsg.fadeOut(function(){ $(this).remove(); }); }, 5000); | |
| 681 | + } | |
| 682 | + } else { | |
| 683 | + // Fallback to after button if price element not found | |
| 684 | + notificationPosition = 'after_button'; | |
| 685 | + } | |
| 686 | + break; | |
| 687 | + | |
| 688 | + case 'in_container': | |
| 689 | + var customContainer = $button.closest('.king-addons-pricing-slider, .king-addons-pricing-sliders') | |
| 690 | + .find('.king-addons-notification-container'); | |
| 691 | + if (customContainer.length) { | |
| 692 | + customContainer.empty(); // Clear previous notifications | |
| 693 | + | |
| 694 | + if (response.success) { | |
| 695 | + // console.log('[KA Slider] Product added to cart:', response.data); | |
| 696 | + // Trigger WooCommerce added_to_cart event for themes/plugins to hook into | |
| 697 | + $(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, $button]); | |
| 698 | + | |
| 699 | + var successMsg = $('<span class="king-addons-slider-notice success">' + (response.data.message || 'Added to cart!') + '</span>'); | |
| 700 | + customContainer.append(successMsg); | |
| 701 | + | |
| 702 | + if (response.data.cart_url) { | |
| 703 | + var viewCartText = (typeof king_addons_slider_vars !== 'undefined' && king_addons_slider_vars.view_cart_text) | |
| 704 | + ? king_addons_slider_vars.view_cart_text : 'View Cart'; | |
| 705 | + var viewCartLink = $('<a href="' + response.data.cart_url + '" class="king-addons-slider-view-cart">' + viewCartText + '</a>'); | |
| 706 | + customContainer.append(viewCartLink); | |
| 707 | + | |
| 708 | + setTimeout(function(){ | |
| 709 | + successMsg.fadeOut(function(){ $(this).remove(); }); | |
| 710 | + viewCartLink.fadeOut(function(){ $(this).remove(); }); | |
| 711 | + }, 5000); | |
| 712 | + } else { | |
| 713 | + setTimeout(function(){ successMsg.fadeOut(function(){ $(this).remove(); }); }, 3000); | |
| 714 | + } | |
| 715 | + } else { | |
| 716 | + var errorMessage = 'Could not add product to cart. Please try again.'; | |
| 717 | + if (response.data && response.data.error_code === 'budget_too_low') { | |
| 718 | + var templateMessage = priceData.budget_too_low_message || 'Your selected budget is too low. Please increase the budget to at least {product_price}.'; | |
| 719 | + var productPrice = response.data.product_price || ''; | |
| 720 | + errorMessage = templateMessage.replace('{product_price}', productPrice); | |
| 721 | + } else if (response.data && response.data.message) { | |
| 722 | + errorMessage = response.data.message; | |
| 723 | + } | |
| 724 | + | |
| 725 | + var errorMsg = $('<span class="king-addons-slider-notice error">' + errorMessage + '</span>'); | |
| 726 | + customContainer.append(errorMsg); | |
| 727 | + setTimeout(function(){ errorMsg.fadeOut(function(){ $(this).remove(); }); }, 5000); | |
| 728 | + } | |
| 729 | + } else { | |
| 730 | + // Fallback to after button if custom container not found | |
| 731 | + notificationPosition = 'after_button'; | |
| 732 | + } | |
| 733 | + break; | |
| 734 | + | |
| 735 | + case 'after_button': | |
| 736 | + default: | |
| 737 | + // This is the original behavior - place notifications after the button | |
| 738 | + if (response.success) { | |
| 739 | + // console.log('[KA Slider] Product added to cart:', response.data); | |
| 740 | + // Trigger WooCommerce added_to_cart event for themes/plugins to hook into | |
| 741 | + $(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, $button]); | |
| 742 | + | |
| 743 | + var successMsg = $('<span class="king-addons-slider-notice success">' + (response.data.message || 'Added to cart!') + '</span>'); | |
| 744 | + successMsg.insertAfter($button); | |
| 745 | + | |
| 746 | + if (response.data.cart_url) { | |
| 747 | + var viewCartText = (typeof king_addons_slider_vars !== 'undefined' && king_addons_slider_vars.view_cart_text) | |
| 748 | + ? king_addons_slider_vars.view_cart_text : 'View Cart'; | |
| 749 | + var viewCartLink = $('<a href="' + response.data.cart_url + '" class="king-addons-slider-view-cart">' + viewCartText + '</a>'); | |
| 750 | + viewCartLink.insertAfter(successMsg); | |
| 751 | + | |
| 752 | + setTimeout(function(){ | |
| 753 | + successMsg.fadeOut(function(){ $(this).remove(); }); | |
| 754 | + viewCartLink.fadeOut(function(){ $(this).remove(); }); | |
| 755 | + }, 5000); | |
| 756 | + } else { | |
| 757 | + setTimeout(function(){ successMsg.fadeOut(function(){ $(this).remove(); }); }, 3000); | |
| 758 | + } | |
| 759 | + } else { | |
| 760 | + var errorMessage = 'Could not add product to cart. Please try again.'; | |
| 761 | + // console.error('[KA Slider] Error adding to cart:', response.data); | |
| 762 | + | |
| 763 | + if (response.data && response.data.error_code === 'budget_too_low') { | |
| 764 | + var templateMessage = priceData.budget_too_low_message || 'Your selected budget is too low. Please increase the budget to at least {product_price}.'; | |
| 765 | + var productPrice = response.data.product_price || ''; | |
| 766 | + errorMessage = templateMessage.replace('{product_price}', productPrice); | |
| 767 | + } else if (response.data && response.data.message) { | |
| 768 | + errorMessage = response.data.message; | |
| 769 | + } | |
| 770 | + | |
| 771 | + var errorMsg = $('<span class="king-addons-slider-notice error">' + errorMessage + '</span>'); | |
| 772 | + errorMsg.insertAfter($button); | |
| 773 | + setTimeout(function(){ errorMsg.fadeOut(function(){ $(this).remove(); }); }, 5000); | |
| 774 | + } | |
| 775 | + break; | |
| 776 | + } | |
| 777 | + }, | |
| 778 | + error: function(jqXHR, textStatus, errorThrown) { | |
| 779 | + $button.removeClass('loading'); | |
| 780 | + // console.error('[KA Slider] AJAX Error:', textStatus, errorThrown); | |
| 781 | + // Show generic user-friendly error for AJAX failures | |
| 782 | + var errorMsg = $('<span class="king-addons-slider-notice error">An error occurred. Please try again later.</span>').insertAfter($button); | |
| 783 | + setTimeout(function(){ errorMsg.fadeOut(function(){ $(this).remove(); }); }, 5000); | |
| 784 | + } | |
| 785 | + }); | |
| 786 | + }); | |
| 787 | + } | |
| 788 | + } | |
| 789 | + | |
| 790 | + // --- Elementor Hook --- (Handles both Editor and Frontend) | |
| 791 | + $(window).on('elementor/frontend/init', function() { | |
| 792 | + var initAction = function($scope) { | |
| 793 | + // Use $scope if provided (in editor), otherwise use document context (frontend) | |
| 794 | + var context = $scope ? $scope : $(document); | |
| 795 | + | |
| 796 | + // Initialize Single Sliders | |
| 797 | + // Find only direct .king-addons-pricing-slider that are NOT .king-addons-pricing-sliders containers | |
| 798 | + context.find('.king-addons-pricing-slider:not(.king-addons-pricing-sliders)').each(function() { | |
| 799 | + var sliderInstance = $(this); | |
| 800 | + // Check if already initialized (e.g., by multiple init calls in editor) | |
| 801 | + if (!sliderInstance.data('ka-slider-initialized')) { | |
| 802 | + initSinglePricingSliderRevisited(sliderInstance); | |
| 803 | + sliderInstance.data('ka-slider-initialized', true); | |
| 804 | + } else { | |
| 805 | + // console.log('[KA Slider] Single slider already initialized, skipping:', sliderInstance[0]); | |
| 806 | + // Optionally re-trigger UI update if needed in editor on changes | |
| 807 | + var rangeInput = sliderInstance.find('.king-addons-pricing-slider__range'); | |
| 808 | + if(rangeInput.length) { | |
| 809 | + sharedUpdateSliderUI(sliderInstance, parseFloat(rangeInput.val())); | |
| 810 | + } | |
| 811 | + } | |
| 812 | + }); | |
| 813 | + | |
| 814 | + // Initialize Multiple Sliders Containers | |
| 815 | + context.find('.king-addons-pricing-sliders').each(function() { | |
| 816 | + var slidersContainer = $(this); | |
| 817 | + if (!slidersContainer.data('ka-slider-initialized')) { | |
| 818 | + initMultiplePricingSlidersRevisited(slidersContainer); | |
| 819 | + slidersContainer.data('ka-slider-initialized', true); | |
| 820 | + } else { | |
| 821 | + // console.log('[KA Slider] Multi-slider container already initialized, skipping:', slidersContainer[0]); | |
| 822 | + // Optionally re-trigger UI update for all children | |
| 823 | + slidersContainer.find('.king-addons-single-slider').each(function() { | |
| 824 | + var singleSliderWrapper = $(this); | |
| 825 | + var rangeInput = singleSliderWrapper.find('.king-addons-pricing-slider__range'); | |
| 826 | + if (rangeInput.length) { | |
| 827 | + sharedUpdateSliderUI(singleSliderWrapper, parseFloat(rangeInput.val())); | |
| 828 | + } | |
| 829 | + }); | |
| 830 | + // And update combined display | |
| 831 | + // Need a way to call updateCombinedDisplayForMulti() here if instance already exists | |
| 832 | + // For now, just re-init might be simpler, or store the update function on the element data | |
| 833 | + } | |
| 834 | + }); | |
| 835 | + }; | |
| 836 | + | |
| 837 | + // --- Elementor Editor Integration --- | |
| 838 | + if (typeof elementorFrontend !== 'undefined' && elementorFrontend.isEditMode()) { | |
| 839 | + elementorFrontend.hooks.addAction('frontend/element_ready/king-addons-pricing-slider.default', function($scope) { | |
| 840 | + // Use timeout to allow elements and data attributes to be fully rendered by Elementor | |
| 841 | + // console.log('[KA Slider] Elementor element_ready hook fired for scope:', $scope[0]); | |
| 842 | + setTimeout(function() { | |
| 843 | + // Clear initialized flag before re-init to handle widget updates | |
| 844 | + $scope.find('.king-addons-pricing-slider, .king-addons-pricing-sliders').removeData('ka-slider-initialized'); | |
| 845 | + initAction($scope); | |
| 846 | + }, 150); // Slightly longer timeout for editor stability | |
| 847 | + }); | |
| 848 | + } else { | |
| 849 | + // --- Frontend Initialization --- | |
| 850 | + // Run on document ready for frontend | |
| 851 | + $(document).ready(function() { | |
| 852 | + initAction(); | |
| 853 | + }); | |
| 854 | + } | |
| 855 | + }); | |
| 856 | + | |
| 857 | +})(jQuery); | |
| 858 | +// --- REMOVE OLD FUNCTIONS --- | |
| 859 | +/* | |
| 860 | +// Old function, replaced by initSinglePricingSliderRevisited | |
| 861 | +function initSinglePricingSlider(sliderWrapper) { | |
| 862 | + // ... implementation removed ... | |
| 863 | +} | |
| 864 | + | |
| 865 | +// Old function, replaced by initMultiplePricingSlidersRevisited | |
| 866 | +function initMultiplePricingSliders(slidersContainer) { | |
| 867 | + // ... implementation removed ... | |
| 868 | +} | |
| 869 | +*/ | |