1/* Pricing page: volume slider, currency switch, compare-all-features toggle. 2 * 3 * Holds no prices. The matrix comes from the <script type="application/json" 4 * id="pricing-data"> block, which Liquid generates from _data/pricing.yml. 5 * The page is already rendered in its default state (250 attendees, USD) in 6 * the HTML, so crawlers and visitors without JavaScript see real prices. This 7 * file only changes values when the visitor moves the slider or the currency. 8 */ 9(function () { 10 function init() { 11 var dataEl = document.getElementById("pricing-data"); 12 if (!dataEl) return; 13 var data; 14 try { data = JSON.parse(dataEl.textContent); } catch (e) { return; } 15 16 var ui = data.ui; 17 var symbols = {}; 18 data.currencies.forEach(function (c) { symbols[c.code] = c.symbol; }); 19 20 var slider = document.getElementById("prSlider"); 21 var sliderTrack = document.getElementById("prSliderTrack"); 22 var volumeLabel = document.getElementById("prVolumeLabel"); 23 var currencyName = document.getElementById("prCurrencyName"); 24 var currencyBtns = document.querySelectorAll(".pr-currency-btn"); 25 var planCards = document.querySelectorAll(".pr-plan"); 26 if (!slider || !volumeLabel) return; 27 28 var state = { idx: data.defaultIndex, currency: data.defaultCurrency }; 29 30 // Same grouping as the Liquid include pricing-number.html, so the numbers 31 // the server renders and the numbers this script renders look identical. 32 function formatNumber(n) { 33 return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ui.sep); 34 } 35 36 function setPrice(card, value) { 37 var priceEl = card.querySelector("[data-price]"); 38 var volumeEl = card.querySelector("[data-volume]"); 39 var symEl = card.querySelector("[data-cur-symbol]"); 40 41 if (symEl) symEl.textContent = symbols[state.currency]; 42 if (volumeEl) volumeEl.textContent = formatNumber(data.volumes[state.idx]); 43 if (!priceEl) return; 44 45 // Fade out, update, fade in for visual feedback 46 priceEl.classList.add("is-updating"); 47 setTimeout(function () { 48 priceEl.textContent = value == null ? ui.noPrice : formatNumber(value); 49 priceEl.classList.remove("is-updating"); 50 }, 90); 51 } 52 53 function update() { 54 var vol = data.volumes[state.idx]; 55 volumeLabel.textContent = ui.volumeLabel.replace("%n%", formatNumber(vol)); 56 if (currencyName) currencyName.textContent = ui.currencyNames[state.currency]; 57 58 // Slider track fill follows the thumb position 59 if (sliderTrack) { 60 sliderTrack.style.width = (state.idx / (data.volumes.length - 1) * 100) + "%"; 61 } 62 63 planCards.forEach(function (card) { 64 var prices = data.plans[card.getAttribute("data-plan")]; 65 if (!prices) return; 66 setPrice(card, prices[state.currency][state.idx]); 67 }); 68 } 69 70 slider.addEventListener("input", function () { 71 state.idx = parseInt(slider.value, 10); 72 update(); 73 }); 74 75 currencyBtns.forEach(function (btn) { 76 btn.addEventListener("click", function () { 77 state.currency = btn.getAttribute("data-currency"); 78 currencyBtns.forEach(function (b) { b.classList.remove("is-active"); }); 79 btn.classList.add("is-active"); 80 update(); 81 }); 82 }); 83 84 // A restored scroll or back navigation can keep a moved slider value. 85 // Sync to it; otherwise the server-rendered state is already correct. 86 var restored = parseInt(slider.value, 10); 87 if (restored !== state.idx) { 88 state.idx = restored; 89 update(); 90 } 91 92 // Compare-all-features toggle 93 var btn = document.getElementById("prTableToggle"); 94 var more = document.getElementById("pricing-table-more"); 95 var label = btn && btn.querySelector(".pr-table-toggle-label"); 96 if (btn && more && label) { 97 btn.addEventListener("click", function () { 98 var open = more.hidden === false; 99 more.hidden = open; 100 btn.setAttribute("aria-expanded", open ? "false" : "true"); 101 label.textContent = open ? ui.compareOpen : ui.compareClose; 102 }); 103 } 104 } 105 106 if (document.readyState === "loading") { 107 document.addEventListener("DOMContentLoaded", init); 108 } else { 109 init(); 110 } 111})();
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.