1function superDollar(str) { 2 if (!str) return str; 3 var clean = str.replace('$', ''); 4 var dot = clean.indexOf('.'); 5 if (dot === -1) return '<span class="co-summary-dollar">$</span>' + clean; 6 return '<span class="co-summary-dollar">$</span>' + clean.slice(0, dot) + '<span class="co-summary-cents">.' + clean.slice(dot + 1) + '</span>'; 7} 8 9// Whole-dollar prices read better without the cents â "$25", not "$25.00". 10// Only a flat-price brand hits this today (Microchurch); every tier on the 11// other brands ends .49 or .99, so they are unaffected. 12function formatMoney(n) { 13 var s = n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); 14 return '$' + s.replace(/\.00$/, ''); 15} 16 17function _storageKey(name) { 18 var prefix = (window.BRAND_CONFIG && window.BRAND_CONFIG.storagePrefix) || 'rnm'; 19 return prefix + '_' + name; 20} 21 22function saveBilling(v) { sessionStorage.setItem(_storageKey('billing'), v); } 23// Brands may open on either cycle: BRAND_CONFIG.defaultBilling picks. Microchurch 24// opens monthly ($25 reads easier than $275); everyone else opens annual. 25function loadBilling() { 26 var fallback = (window.BRAND_CONFIG && window.BRAND_CONFIG.defaultBilling) || 'annual'; 27 return sessionStorage.getItem(_storageKey('billing')) || fallback; 28} 29 30// A campaign link may preset the billing cycle: /checkout/?billing=monthly. 31// Called once on step 1, BEFORE the first loadBilling(), and deliberately 32// writes through to session storage rather than just returning the value: 33// step 2 reads the cycle back out of storage to pick the Chargebee plan id, so 34// a cycle that only reached the toggle would show one price and charge the 35// other. An email's stated intent outranks a cycle stored on an earlier visit, 36// which is why this overwrites. 37// 38// Case and stray whitespace are forgiven ("?billing=Monthly" is a link someone 39// hand-typed, not a different intent), but anything that is not one of the two 40// cycles is ignored outright â writing a junk value here would flow straight 41// into getTierData and the plan id lookup. 42function applyBillingFromUrl() { 43 var raw; 44 try { 45 raw = new URLSearchParams(window.location.search).get('billing'); 46 } catch (e) { 47 return null; 48 } 49 if (raw == null) return null; 50 var cycle = String(raw).trim().toLowerCase(); 51 if (cycle !== 'annual' && cycle !== 'monthly') return null; 52 saveBilling(cycle); 53 return cycle; 54} 55 56function saveTier(index) { sessionStorage.setItem(_storageKey('tier'), String(index)); } 57// The fallback is clamped to the tier count: a brand with fewer than four tiers 58// (Microchurch has one) would otherwise get index 3 and read TIERS[3] as 59// undefined. BRAND_CONFIG.defaultTierIndex overrides where a brand wants the 60// slider to open somewhere other than the fourth tier. 61function loadTier() { 62 var raw = sessionStorage.getItem(_storageKey('tier')); 63 var i = parseInt(raw, 10); 64 if (!isNaN(i) && i >= 0 && i < TIERS.length) return i; 65 var wanted = (window.BRAND_CONFIG && window.BRAND_CONFIG.defaultTierIndex); 66 if (wanted == null) wanted = 3; 67 return Math.max(0, Math.min(wanted, TIERS.length - 1)); 68} 69 70function getTierData(index, billing) { 71 var t = TIERS[index]; 72 var last = TIERS.length - 1; 73 var isAnnual = (billing || loadBilling()) === 'annual'; 74 75 var monthly = isAnnual ? t.price : t.monthlyPrice; 76 var annual = t.annualTotal != null ? t.annualTotal : (t.price != null ? t.price * 12 : null); 77 var whole = monthly != null ? Math.floor(monthly).toLocaleString('en-US') : null; 78 var cents = monthly != null 79 ? String(Math.round((monthly - Math.floor(monthly)) * 100)).padStart(2, '0') 80 : null; 81 // null rather than '00' so callers can omit the cents span entirely. 82 if (cents === '00') cents = null; 83 84 var savingsPct = 0; 85 if (t.annualTotal != null && t.monthlyPrice != null) { 86 savingsPct = Math.round((1 - t.annualTotal / (t.monthlyPrice * 12)) * 100); 87 } else if (t.price != null && t.monthlyPrice != null) { 88 savingsPct = Math.round((1 - t.price / t.monthlyPrice) * 100); 89 } 90 91 var due = new Date(); 92 due.setDate(due.getDate() + 30); 93 var firstPaymentDate = due.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' }); 94 95 return { 96 tier: t, 97 tierIndex: index, 98 // A single-tier brand has last === 0; dividing by it yields NaN. 99 pctStr: (last > 0 ? (index / last * 100) : 0).toFixed(3) + '%', 100 tierLabel: t.label, 101 isStd: t.kind === 'std', 102 isCall: t.kind === 'call',
103 isAnnual: isAnnual, 104 monthlyStr: monthly != null ? formatMoney(monthly) : null, 105 monthlyWhole: whole, 106 monthlyCents: cents, 107 annualStr: annual != null ? formatMoney(annual) : null, 108 savingsPct: savingsPct, 109 firstPaymentDate: firstPaymentDate, 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.