1/** 2 * Pro Domain Dropdown Functionality 3 */ 4document.addEventListener('DOMContentLoaded', function() { 5 const dropdowns = document.querySelectorAll('.pricing-card__custom-dropdown'); 6 7 function getPromoPercentage(section, cardType, optionNumber) { 8 const percentageKey = `${section}_${cardType}_option${optionNumber}_percentage`; 9 const fromSettings = window.pricingBlockSettings?.[percentageKey]; 10 11 if (fromSettings !== undefined && fromSettings !== null && fromSettings !== '') { 12 return parseInt(fromSettings, 10); 13 } 14 15 if (cardType === 'pro' && optionNumber === 1) { 16 return section === 'lifetime' ? 21 : 25; 17 } 18 19 if (cardType === 'pro' && optionNumber === 2) { 20 return 30; 21 } 22 23 return null; 24 } 25 26 function updateDomainPromoDisplay(cardElement, section, cardType, optionNumber) { 27 if (!isCardPromoActive(cardElement)) { 28 return; 29 } 30 31 const percentage = getPromoPercentage(section, cardType, optionNumber); 32 33 if (!percentage) { 34 return; 35 } 36 37 const pillarText = percentage + '% off'; 38 const percentagePillar = cardElement.querySelector('.pricing-card__domain-promo-pillar-text'); 39 if (percentagePillar) { 40 percentagePillar.textContent = pillarText; 41 } 42 43 const promoBadgeText = cardElement.querySelector('.pricing-card__promo-badge-text'); 44 const percentageSpan = promoBadgeText?.querySelector('.percentage'); 45 if (percentageSpan) { 46 promoBadgeText.innerHTML = 47 `<span class="percentage">${percentage}%</span><span class="off-text">Off</span>`; 48 } 49 } 50 51 // Initialize on page load 52 dropdowns.forEach(dropdown => { 53 initializeDropdown(dropdown); 54 }); 55 56 // Initialize button attributes for Pro/Elite cards on page load 57 initializeButtons(); 58 59 // Handle custom dropdown interactions 60 dropdowns.forEach(dropdown => { 61 const selected = dropdown.querySelector('.custom-dropdown__selected'); 62 const options = dropdown.querySelectorAll('.custom-dropdown__option'); 63 64 // Toggle dropdown on click 65 selected.addEventListener('click', function(e) { 66 e.stopPropagation(); 67 68 // Check if this dropdown is already open 69 const isCurrentlyOpen = dropdown.classList.contains('open'); 70 71 // Close all dropdowns first 72 closeAllDropdowns(); 73 74 // If this dropdown wasn't open, open it 75 if (!isCurrentlyOpen) { 76 dropdown.classList.add('open'); 77 } 78 }); 79 80 // Handle option selection 81 options.forEach(option => { 82 option.addEventListener('click', function(e) { 83 e.stopPropagation(); 84 const value = this.dataset.value; 85 const text = this.textContent; 86 87 // Update selected text 88 selected.textContent = text; 89 90 // Update selected state 91 options.forEach(opt => opt.classList.remove('selected')); 92 this.classList.add('selected'); 93 94 // Close dropdown 95 dropdown.classList.remove('open'); 96 97 // Trigger pricing update 98 updateDropdown(dropdown, value, { refreshPrices: true }); 99 }); 100 }); 101 }); 102 103 // Make entire domain box clickable to open dropdown 104 const domainBoxes = document.querySelectorAll('.pricing-card__domain-info--pro, .pricing-card--elite .pricing-card__domain-info'); 105 domainBoxes.forEach(box => { 106 box.addEventListener('click', function(e) { 107 const customDropdown = box.querySelector('.pricing-card__custom-dropdown'); 108 if (customDropdown && e.target !== customDropdown && !customDropdown.contains(e.target)) { 109 closeAllDropdowns(); 110 customDropdown.classList.toggle('open'); 111 } 112 }); 113 }); 114 115 // Close dropdowns when clicking outside 116 document.addEventListener('click', function(e) { 117 if (!e.target.closest('.pricing-card__domain-info')) { 118 closeAllDropdowns(); 119 } 120 }); 121 122 function closeAllDropdowns() { 123 dropdowns.forEach(dropdown => { 124 dropdown.classList.remove('open'); 125 }); 126 } 127 128 function getDefaultDomainValue(cardElement, cardType) { 129 const fromCard = cardElement?.getAttribute('data-default-domain-value'); 130 if (fromCard) { 131 return fromCard; 132 } 133 134 const settingsKey = cardType === 'pro' ? 'pro_default_domain_option' : 'elite_default_domain_option'; 135 const optionSetting = window.pricingBlockSettings?.[settingsKey] || 'option2'; 136 137 if (cardType === 'pro') { 138 return optionSetting === 'option1' ? '1' : '5'; 139 } 140 141 return optionSetting === 'option1' ? '20' : 'unlimited'; 142 } 143 144 function isCardPromoActive(cardElement) { 145 return cardElement?.getAttribute('data-card-promo') !== 'no'; 146 } 147 148 function getDefaultDomainLabel(cardElement, cardType, defaultValue) { 149 const matchingOption = cardElement?.querySelector( 150 `.custom-dropdown__option[data-value="${defaultValue}"]` 151 ); 152 if (matchingOption) { 153 return matchingOption.textContent.trim(); 154 } 155 156 if (cardType === 'pro') { 157 return defaultValue === '1' 158 ? (window.ameliaStrings?.['1_domain'] || '1 domain') 159 : (window.ameliaStrings?.['5_domains'] || '5 domains'); 160 } 161 162 return defaultValue === '20' 163 ? (window.ameliaStrings?.['20_domains'] || '20 domains') 164 : (window.ameliaStrings?.unlimited_domains || 'Unlimited domains'); 165 } 166 167 function initializeButtons() { 168 const proCards = document.querySelectorAll('.pricing-card--pro'); 169 const eliteCards = document.querySelectorAll('.pricing-card--elite'); 170
171 [...proCards, ...eliteCards].forEach(card => { 172 const cardType = card.classList.contains('pricing-card--pro') ? 'pro' : 'elite'; 173 const section = card.getAttribute('data-card-mode') || 'annual'; 174 const defaultValue = getDefaultDomainValue(card, cardType); 175 const optionNumber = (defaultValue === '1' || defaultValue === '20') ? 1 : 2; 176 const pricingKey = `${section}_${cardType}_option${optionNumber}`; 177 const pricingData = window.pricingBlockSettings?.[pricingKey]; 178 179 if (pricingData) { 180 const button = card.querySelector('.pricing-card__button'); 181 if (button) { 182 if (pricingData.button_id) button.setAttribute('id', pricingData.button_id); 183 if (pricingData.data_plan) button.setAttribute('data-plan', pricingData.data_plan); 184 } 185 } 186 }); 187 } 188 189 function initializeDropdown(dropdown) { 190 const cardElement = dropdown.closest('.pricing-card'); 191 const cardType = dropdown.dataset.cardType || (cardElement.classList.contains('pricing-card--pro') ? 'pro' : 'elite'); 192 const defaultValue = getDefaultDomainValue(cardElement, cardType); 193 const defaultText = getDefaultDomainLabel(cardElement, cardType, defaultValue); 194 195 const selectedElement = dropdown.querySelector('.custom-dropdown__selected'); 196 if (selectedElement) { 197 selectedElement.textContent = defaultText; 198 } 199 200 const options = dropdown.querySelectorAll('.custom-dropdown__option'); 201 options.forEach(option => { 202 option.classList.remove('selected'); 203 if (option.dataset.value === defaultValue) { 204 option.classList.add('selected'); 205 } 206 }); 207 208 // Skip full sync when PHP already rendered the correct default (prevents promo badge flash) 209 if (!cardElement.hasAttribute('data-default-domain-value')) { 210 updateDropdown(dropdown, defaultValue, { refreshPrices: false }); 211 } 212 } 213 214 function updateDropdown(dropdown, selectedValue, options) { 215 const refreshPrices = !options || options.refreshPrices !== false; 216 const cardElement = dropdown.closest('.pricing-card'); 217 const cardType = dropdown.dataset.cardType || (cardElement.classList.contains('pricing-card--pro') ? 'pro' : 'elite'); 218 const section = cardElement.getAttribute('data-card-mode') || 'annual'; 219 const optionNumber = (selectedValue === '1' || selectedValue === '20') ? 1 : 2; 220 221 const option1 = cardElement.querySelector('.pricing-option-1'); 222 const option2 = cardElement.querySelector('.pricing-option-2'); 223 224 if (optionNumber === 1) { 225 option1?.classList.add('active'); 226 option2?.classList.remove('active'); 227 } else { 228 option1?.classList.remove('active'); 229 option2?.classList.add('active'); 230 } 231 232 updateDomainPromoDisplay(cardElement, section, cardType, optionNumber); 233 234 const pricingKey = `${section}_${cardType}_option${optionNumber}`; 235 const pricingData = window.pricingBlockSettings?.[pricingKey]; 236 const dataPlan = pricingData?.data_plan; 237 238 if (pricingData) { 239 const buttonId = pricingData.button_id; 240 241 const button = cardElement.querySelector('.pricing-card__button'); 242 if (button) { 243 if (buttonId) button.setAttribute('id', buttonId); 244 if (dataPlan) button.setAttribute('data-plan', dataPlan); 245 } 246 } 247 248 if (refreshPrices) { 249 const activeOption = optionNumber === 1 ? option1 : option2; 250 const priceElements = activeOption 251 ? activeOption.querySelectorAll('.pricing-paddle-price') 252 : cardElement.querySelectorAll('.pricing-paddle-price'); 253 254 priceElements.forEach((priceElement) => { 255 if (pricingData && dataPlan) { 256 priceElement.setAttribute('data-plan', dataPlan); 257 } 258 259 priceElement.removeAttribute('data-billing-price-rendered'); 260 }); 261 262 if (typeof window.refreshBillingPricePreviews === 'function') { 263 window.refreshBillingPricePreviews(); 264 } 265 } 266 } 267 268 // Mobile slider functionality 269 function initializeMobileSlider() { 270 const pricingGrid = document.querySelector('.pricing-block__grid'); 271 const navDots = document.querySelectorAll('.nav-dot'); 272 const pricingCards = document.querySelectorAll('.pricing-card:not([style*="display: none"])'); 273 274 if (!pricingGrid || !navDots.length || !pricingCards.length) return; 275 276 let currentSlide = 0; 277 let isScrolling = false;
278 let cachedCardWidth = pricingCards[0].offsetWidth + 20; 279 280 pricingGrid.scrollLeft = 0; 281 282 navDots.forEach((dot, index) => { 283 if (index < pricingCards.length) { 284 dot.style.display = 'block'; 285 if (index === 0) dot.classList.add('active'); 286 } else { 287 dot.style.display = 'none'; 288 } 289 }); 290 291 navDots.forEach((dot, index) => { 292 if (index < pricingCards.length) { 293 dot.addEventListener('click', () => { 294 scrollToSlide(index); 295 }); 296 } 297 }); 298 299 pricingGrid.addEventListener('scroll', () => { 300 if (isScrolling) return; 301 302 const scrollLeft = pricingGrid.scrollLeft; 303 const slideIndex = Math.round(scrollLeft / cachedCardWidth); 304 305 if (slideIndex !== currentSlide && slideIndex >= 0 && slideIndex < pricingCards.length) { 306 updateActiveDot(slideIndex); 307 } 308 }); 309 310 let startX = 0; 311 let scrollStart = 0; 312 313 pricingGrid.addEventListener('touchstart', (e) => { 314 startX = e.touches[0].clientX; 315 scrollStart = pricingGrid.scrollLeft; 316 }); 317 318 pricingGrid.addEventListener('touchmove', (e) => { 319 if (!startX) return; 320 const currentX = e.touches[0].clientX; 321 const diff = startX - currentX; 322 pricingGrid.scrollLeft = scrollStart + diff; 323 }); 324 325 pricingGrid.addEventListener('touchend', () => { 326 startX = 0; 327 scrollStart = 0; 328 const scrollLeft = pricingGrid.scrollLeft; 329 const nearestSlide = Math.round(scrollLeft / cachedCardWidth); 330 scrollToSlide(nearestSlide); 331 }); 332 333 function scrollToSlide(slideIndex) { 334 if (slideIndex < 0 || slideIndex >= pricingCards.length) return; 335 336 isScrolling = true; 337 const scrollLeft = slideIndex * cachedCardWidth; 338 339 pricingGrid.scrollTo({ 340 left: scrollLeft, 341 behavior: 'smooth' 342 }); 343 344 updateActiveDot(slideIndex); 345 346 setTimeout(() => { 347 isScrolling = false; 348 }, 300); 349 } 350 351 function updateActiveDot(slideIndex) { 352 navDots.forEach((dot, index) => { 353 dot.classList.toggle('active', index === slideIndex); 354 }); 355 currentSlide = slideIndex; 356 } 357 358 window.addEventListener('resize', () => { 359 cachedCardWidth = pricingCards[0].offsetWidth + 20; 360 }); 361 } 362 363 const isElementorEditor = typeof elementor !== 'undefined' || document.body.classList.contains('elementor-editor-active'); 364 let isMobileSliderActive = false; 365 366 if (!isElementorEditor && window.innerWidth <= 768) { 367 initializeMobileSlider(); 368 isMobileSliderActive = true; 369 } 370 371 let resizeTimeout; 372 window.addEventListener('resize', () => { 373 clearTimeout(resizeTimeout); 374 resizeTimeout = setTimeout(() => { 375 if (isElementorEditor) return; 376 377 const shouldBeMobile = window.innerWidth <= 768; 378 if (shouldBeMobile && !isMobileSliderActive) { 379 initializeMobileSlider(); 380 isMobileSliderActive = true; 381 } else if (!shouldBeMobile && isMobileSliderActive) { 382 isMobileSliderActive = false; 383 } 384 }, 150); 385 }); 386});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.