1/** 2 * Pricing Block JavaScript 3 * Handles annual/lifetime toggle and Paddle integration 4 */ 5 6document.addEventListener('DOMContentLoaded', function() { 7 const pricingToggles = document.querySelectorAll('[data-pricing-toggle]'); 8 9 pricingToggles.forEach(toggle => { 10 const options = toggle.querySelectorAll('.pricing-toggle__option'); 11 const blockContainer = toggle.closest('.pricing-block') || toggle.closest('.pricing-tables'); 12 13 if (!blockContainer) return; 14 15 const grid = blockContainer.querySelector('[data-pricing-grid]'); 16 17 if (!grid) return; 18 19 options.forEach(option => { 20 option.addEventListener('click', function() { 21 const mode = this.getAttribute('data-mode'); 22 23 // Update active state 24 options.forEach(opt => opt.classList.remove('pricing-toggle__option--active')); 25 this.classList.add('pricing-toggle__option--active'); 26 27 // Update cards visibility and pricing 28 updateCardsForMode(grid, mode); 29 }); 30 }); 31 }); 32}); 33 34/** 35 * Update cards visibility based on pricing mode 36 * @param {Element} grid - The pricing grid container 37 * @param {string} mode - 'annual' or 'lifetime' 38 */ 39function updateCardsForMode(grid, mode) { 40 const cards = grid.querySelectorAll('.pricing-card'); 41 42 cards.forEach(card => { 43 const cardMode = card.getAttribute('data-card-mode'); 44 45 if (cardMode === mode) { 46 card.style.display = 'flex'; 47 card.classList.remove('pricing-card--hidden'); 48 } else { 49 card.style.display = 'none'; 50 card.classList.add('pricing-card--hidden'); 51 } 52 }); 53} 54 55// Expose function globally for comparison table sync 56window.updatePricingBlockForMode = updateCardsForMode; 57 58/** 59 * Update cards visibility based on pricing mode 60 * @param {Element} grid - The pricing grid container 61 * @param {string} mode - 'annual' or 'lifetime' 62 */ 63function updateCardsVisibility(grid, mode) { 64 const cards = grid.querySelectorAll('.pricing-card'); 65 66 cards.forEach(card => { 67 const showInAnnual = card.getAttribute('data-show-annual') === 'yes'; 68 const showInLifetime = card.getAttribute('data-show-lifetime') === 'yes'; 69 70 let shouldShow = false; 71 72 if (mode === 'annual' && showInAnnual) { 73 shouldShow = true; 74 } else if (mode === 'lifetime' && showInLifetime) { 75 shouldShow = true; 76 } 77 78 if (shouldShow) { 79 card.style.display = 'flex'; 80 card.classList.remove('pricing-card--hidden'); 81 } else { 82 card.style.display = 'none'; 83 card.classList.add('pricing-card--hidden'); 84 } 85 }); 86} 87 88/** 89 * Handle Show More/Less functionality for pricing card features 90 */ 91function initShowMoreButtons() { 92 const showMoreButtons = document.querySelectorAll('[data-show-more]'); 93 94 showMoreButtons.forEach(button => { 95 const card = button.closest('.pricing-card'); 96 if (!card) return; 97 98 // Wrap hidden features in a container for smooth animation 99 const hiddenFeatures = card.querySelectorAll('.pricing-card__feature-item--hidden'); 100 if (hiddenFeatures.length === 0) return; 101 102 const container = document.createElement('div'); 103 container.className = 'pricing-card__hidden-features-container'; 104 hiddenFeatures[0].parentNode.insertBefore(container, hiddenFeatures[0]); 105 hiddenFeatures.forEach(f => container.appendChild(f)); 106 107 button.addEventListener('click', function(e) { 108 e.preventDefault(); 109 110 const isExpanded = this.classList.contains('active'); 111 112 if (isExpanded) { 113 // Collapse: pin current height then animate to 0 114 container.style.maxHeight = container.scrollHeight + 'px'; 115 requestAnimationFrame(() => { 116 requestAnimationFrame(() => { 117 container.style.maxHeight = '0'; 118 container.style.opacity = '0'; 119 }); 120 }); 121 this.classList.remove('active'); 122 this.querySelector('.show-more-text').textContent = 'Show More'; 123 } else { 124 // Expand: animate to full height then release constraint 125 container.style.maxHeight = container.scrollHeight + 'px'; 126 container.style.opacity = '1'; 127 container.addEventListener('transitionend', function onEnd(ev) { 128 if (ev.propertyName === 'max-height') { 129 container.style.maxHeight = 'none'; 130 container.removeEventListener('transitionend', onEnd); 131 } 132 }); 133 this.classList.add('active'); 134 this.querySelector('.show-more-text').textContent = 'Show Less'; 135 } 136 }); 137 }); 138} 139 140// Initialize show more buttons on page load 141document.addEventListener('DOMContentLoaded', function() { 142 initShowMoreButtons(); 143});
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.