CryptoRetail
€ Bitcoin Calculator

If I Bought Dogecoin
1 Year Ago

See exactly what a $1,000 investment in Bitcoin on this date would be worth today — and when you should have sold.

What would $1,000 in Dogecoin 1 year ago be worth today?

Dogecoin (DOGE) has been one of the most talked-about assets in crypto. If you had invested $1,000 in Dogecoin on ... — exactly 1 year ago — the interactive chart below shows precisely what that investment would be worth at today's price, where the peak profit moment occurred, and how the market has moved since.

Use the calculator below to change the investment amount or explore a different date. You can also compare Dogecoin against Bitcoin, Ethereum, Solana, XRP, and the S&P 500 benchmark.

Dogecoin — $1,000 invested 1 year ago
Weekly closing price · history since ...
📈

Peak profit moment

Loading peak data...

🔴 Live Market Conditions

Where the market stands right now — updated daily.

Total Market Cap
Loading
BTC Dominance
of market
Fear & Greed
Loading

Ready to act on this data?

These are the platforms trusted by serious crypto investors.

📋 Try a different amount or date
Adjust the investment amount or pick any date to recalculate.
Select a date 📅
Jan 2022
SuMoTuWeThFrSa
1 year ago 2 years ago 3 years ago 4 years ago 5 years ago
Fetching price data...

Frequently Asked Questions

What would $1,000 in Dogecoin 1 year ago be worth today?

The exact figure is shown live in the chart above and updates automatically with Dogecoin's current price. Investing in Dogecoin 1 year ago meant entering the market on .... The interactive chart above shows exactly how that investment has performed since then.

When was the best time to sell Dogecoin bought 1 year ago?

The green dot on the chart marks the peak profit moment — the exact date when your $1,000 investment would have been worth the most. Based on the live price data above, the peak for this 1 year ago window was shown on the chart above, when your $1,000 would have been worth shown on the chart above.

How accurate is this Dogecoin calculator?

All price data is sourced from Yahoo Finance using weekly closing prices. Results are historical and for informational purposes only. The calculator does not account for exchange fees, taxes, or the exact timing of trades within a trading day.

Has Dogecoin been a good investment over the past 1 year?

The chart above shows the full picture — including any peaks, corrections, and recoveries since .... Use the calculator to see the current value of your specific investment amount. The chart above shows the full journey.

Where can I buy Dogecoin today?

The most popular and trusted platforms for buying Dogecoin are Coinbase (best for beginners) and Kraken (lower fees, wider coin selection). Both are regulated exchanges with strong security track records. See the recommended platforms above.

Disclaimer: This tool displays historical market data for informational and educational purposes only. Past performance is not indicative of future results. All calculations are based on weekly closing prices sourced from Yahoo Finance. Market sentiment indicators (Fear & Greed Index, Altcoin Season Index) are provided by CoinMarketCap. Nothing on this page constitutes financial advice, investment advice, or a recommendation to buy or sell any asset. Always conduct your own research before making any investment decision.

+ (n / 1e6).toFixed(2) + 'M'; if (Math.abs(n) >= 1e3) return ' function fmtDate(s) { if (!s) return '—'; const d = new Date(s + 'T00:00:00Z'); return d.toLocaleDateString('en-US', {day: 'numeric', month: 'short', year: 'numeric', timeZone: 'UTC'}); } // ─── FETCH YAHOO FINANCE ────────────────────────────────────────────────────── async function fetchPrices(symbol, range='5y') { const key = symbol + range; if (priceCache[key]) return priceCache[key]; // Uses proxy.php on the same server to avoid CORS issues const url = `https://cryptoretail.store/proxy.php?symbol=${encodeURIComponent(symbol)}&range=${encodeURIComponent(range)}`; const res = await fetch(url); if (!res.ok) throw new Error('HTTP ' + res.status); const data = await res.json(); if (data.error) throw new Error(data.error); priceCache[key] = data.points; return data.points; } // ─── DRAW CHART ─────────────────────────────────────────────────────────────── function drawChart(canvasId, labels, values, color, peakIdx, peakVal, existingChart) { const ctx = document.getElementById(canvasId).getContext('2d'); if (existingChart) existingChart.destroy(); const h = canvasId === 'exampleChart' ? 320 : 280; const dataMax = Math.max(...values.filter(v => v !== null && v !== undefined)); const yMax = dataMax * 1.08; const grad = ctx.createLinearGradient(0, 0, 0, h); grad.addColorStop(0, color + '40'); grad.addColorStop(1, color + '00'); const annotations = {}; if (peakIdx !== undefined && peakVal !== undefined) { annotations['peakLine'] = { type: 'line', xMin: peakIdx, xMax: peakIdx, borderColor: 'rgba(63,185,80,0.65)', borderWidth: 2, borderDash: [4,4] }; } return new Chart(ctx, { type: 'line', data: { labels, datasets: [{ data: values, borderColor: color, backgroundColor: grad, borderWidth: 2, pointRadius: 0, pointHoverRadius: 5, fill: true, tension: 0.3 }, { // Peak dot — a single highlighted point at the peak index data: values.map((v, i) => i === peakIdx ? v : null), borderColor: '#3fb950', backgroundColor: '#3fb950', pointRadius: values.map((v, i) => i === peakIdx ? 7 : 0), pointHoverRadius: values.map((v, i) => i === peakIdx ? 9 : 0), borderWidth: 0, fill: false, tension: 0.3, pointStyle: 'circle', showLine: false }] }, options: { responsive: true, maintainAspectRatio: false, layout: { padding: { top: 15 } }, interaction: {mode: 'index', intersect: false}, plugins: { legend: {display: false}, tooltip: { backgroundColor: '#161b22', borderColor: '#30363d', borderWidth: 1, titleColor: '#8b949e', bodyColor: '#e6edf3', callbacks: { title: ctx => fmtDate(ctx[0].label), label: ctx => `Value: ${fmt(ctx.raw)}` } }, annotation: Object.keys(annotations).length ? {annotations} : {} }, scales: { x: { grid: {color: 'rgba(48,54,61,0.5)'}, ticks: { color: '#8b949e', maxTicksLimit: 7, callback: (val, idx, ticks) => { const lbl = ticks[idx]?.label; if (!lbl) return ''; return new Date(lbl + 'T00:00:00Z').getFullYear(); } } }, y: { grid: {color: 'rgba(48,54,61,0.5)'}, ticks: {color: '#8b949e', callback: v => fmt(v)}, max: yMax } } } }); } // ─── BUILD EXAMPLE CARDS ───────────────────────────────────────────────────── function buildCards() { const grid = document.getElementById('examplesGrid'); EXAMPLES.forEach((ex, i) => { const card = document.createElement('div'); card.className = 'example-card' + (i === 0 ? ' active' : ''); card.style.setProperty('--card-color', ex.color); card.id = 'card-' + i; card.onclick = () => selectExample(i); if (ex.isSP500) card.classList.add('sp-card'); card.innerHTML = ex.isSP500 ? `
${ex.icon}
${ex.name}
${ex.ticker}
Benchmark
$1,000 invested 5 years ago
Loading...
How does crypto compare to traditional markets?
` : `
${ex.icon}
${ex.name}
${ex.ticker}
$1,000 invested 5 years ago
Loading...
`; grid.appendChild(card); }); } // ─── LOAD ALL EXAMPLES ──────────────────────────────────────────────────────── async function loadExamples() { for (let i = 0; i < EXAMPLES.length; i++) { const ex = EXAMPLES[i]; try { const prices = await fetchPrices(ex.symbol, '5y'); if (!prices || prices.length < 2) continue; const coinsOwned = 1000 / prices[0].price; const currentValue = coinsOwned * prices[prices.length - 1].price; const roi = ((currentValue - 1000) / 1000) * 100; let peakValue = 0, peakDate = ''; prices.forEach(p => { const v = coinsOwned * p.price; if (v > peakValue) { peakValue = v; peakDate = p.date; } }); EXAMPLES[i] = {...ex, prices, coinsOwned, currentValue, roi, peakValue, peakDate}; document.getElementById('val-' + i).textContent = fmt(currentValue); const roiEl = document.getElementById('roi-' + i); roiEl.textContent = fmtPct(roi); roiEl.className = 'example-roi ' + (roi >= 0 ? 'roi-positive' : 'roi-negative'); if (!ex.isSP500) { const peakRow = document.getElementById('peak-row-' + i); if (peakRow) { peakRow.style.display = 'block'; document.getElementById('peak-val-' + i).textContent = fmt(peakValue); document.getElementById('peak-dt-' + i).textContent = fmtDate(peakDate); } } if (i === 0) selectExample(0); } catch(e) { document.getElementById('val-' + i).textContent = 'Unavailable'; } } } // ─── SELECT EXAMPLE ─────────────────────────────────────────────────────────── function selectExample(i) { activeIdx = i; document.querySelectorAll('.example-card').forEach((c, j) => c.classList.toggle('active', j === i)); const ex = EXAMPLES[i]; if (!ex.prices) return; document.getElementById('exampleChartTitle').textContent = `${ex.name} — $1,000 invested 5 years ago`; const labels = ex.prices.map(p => p.date); const values = ex.prices.map(p => ex.coinsOwned * p.price); const peakIdx = values.indexOf(Math.max(...values)); exampleChart = drawChart('exampleChart', labels, values, ex.color, peakIdx, ex.peakValue, exampleChart); document.getElementById('peakTitle').textContent = `${ex.name} — Peak profit moment`; document.getElementById('peakDesc').textContent = `If you had sold at the peak, your $1,000 would have been worth ${fmt(ex.peakValue)}`; document.getElementById('peakAmount').textContent = fmt(ex.peakValue); document.getElementById('peakDate').textContent = fmtDate(ex.peakDate); } // ─── CUSTOM CALCULATOR ──────────────────────────────────────────────────────── async function runCalculation() { const symbol = document.getElementById('coinSelect').value; const coinName = document.getElementById('coinSelect').selectedOptions[0].dataset.name; const amount = parseFloat(document.getElementById('amountInput').value); const date = document.getElementById('dateInput').value; if (!amount || amount <= 0) { showErr('Please enter a valid investment amount.'); return; } if (!date) { showErr('Please select a purchase date.'); return; } const today = localDateStr(new Date()); // Allow up to 5 years + 3 days to account for any timezone offset worldwide const minDate = new Date(); minDate.setFullYear(minDate.getFullYear() - 5); minDate.setDate(minDate.getDate() - 3); if (date > today) { showErr('Purchase date cannot be in the future.'); return; } if (date < localDateStr(minDate)) { showErr('Please select a date within the last 5 years.'); return; } document.getElementById('calcLoading').style.display = 'block'; document.getElementById('resultBox').style.display = 'none'; document.getElementById('calcError').style.display = 'none'; document.getElementById('calcBtn').disabled = true; try { const prices = await fetchPrices(symbol, '5y'); const afterDate = prices.filter(p => p.date >= date); if (!afterDate.length) { showErr('No price data available for this date. Try a more recent date.'); return; } const buyPt = afterDate[0]; const curPt = prices[prices.length - 1]; const coinsOwned = amount / buyPt.price; const currentValue = coinsOwned * curPt.price; const profit = currentValue - amount; const roi = (profit / amount) * 100; const relevant = prices.filter(p => p.date >= buyPt.date); const labels = relevant.map(p => p.date); const values = relevant.map(p => coinsOwned * p.price); let peakValue = 0, peakDate = '', peakIdx = 0; values.forEach((v, i) => { if (v > peakValue) { peakValue = v; peakDate = labels[i]; peakIdx = i; } }); document.getElementById('resultHeadline').innerHTML = `If you invested ${fmt(amount)} in ${coinName} on ${fmtDate(date)}, it would be worth ${fmt(currentValue)} today.`; document.getElementById('resultStats').innerHTML = `
Invested
${fmt(amount)}
Current Value
${fmt(currentValue)}
Profit / Loss
${fmt(profit)}
ROI
${fmtPct(roi)}
Peak Value
${fmt(peakValue)}
Peak Date
${fmtDate(peakDate)}
`; const color = EXAMPLES.find(e => e.symbol === symbol)?.color || '#f7931a'; customChart = drawChart('customChart', labels, values, color, peakIdx, peakValue, customChart); window._lastResult = {coinName, amount, date, currentValue, profit, roi, peakValue, peakDate}; document.getElementById('resultBox').style.display = 'block'; document.getElementById('resultBox').scrollIntoView({behavior:'smooth', block:'start'}); } catch(e) { showErr('Could not fetch price data. Please try again or select a different coin or date.'); console.error(e); } finally { document.getElementById('calcLoading').style.display = 'none'; document.getElementById('calcBtn').disabled = false; } } function showErr(msg) { document.getElementById('calcLoading').style.display = 'none'; document.getElementById('calcBtn').disabled = false; const el = document.getElementById('calcError'); el.textContent = msg; el.style.display = 'block'; } // ─── SHARE ──────────────────────────────────────────────────────────────────── function copyResult() { const r = window._lastResult; if (!r) return; const t = `If I had invested ${fmt(r.amount)} in ${r.coinName} on ${fmtDate(r.date)}, it would be worth ${fmt(r.currentValue)} today (${fmtPct(r.roi)} ROI). Peak was ${fmt(r.peakValue)} on ${fmtDate(r.peakDate)}. Calculate yours at cryptoretail.store`; navigator.clipboard.writeText(t).then(() => alert('Copied to clipboard!')); } function shareOnX() { const r = window._lastResult; if (!r) return; const t = `If I invested ${fmt(r.amount)} in ${r.coinName} on ${fmtDate(r.date)}, it would be worth ${fmt(r.currentValue)} today (${fmtPct(r.roi)} ROI) 🚀\n\nCalculate yours 👇`; window.open(`https://twitter.com/intent/tweet?text=${encodeURIComponent(t)}&url=${encodeURIComponent('https://cryptoretail.store')}`, '_blank'); } // ─── INIT ───────────────────────────────────────────────────────────────────── // Returns YYYY-MM-DD in the user's LOCAL timezone (avoids UTC-offset boundary issues) function localDateStr(d) { const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); return `${y}-${m}-${day}`; } // ─── MARKET PULSE STRIP ─────────────────────────────────────────────────────────────── function fgClass(v) { if (v < 20) return 'pb-efear'; if (v < 40) return 'pb-fear'; if (v < 60) return 'pb-neutral'; if (v < 80) return 'pb-greed'; return 'pb-egreed'; } function asiClass(v) { if (v < 25) return 'pb-fear'; if (v < 75) return 'pb-neutral'; return 'pb-greed'; } function asiLabel(v) { if (v < 25) return 'Bitcoin Season'; if (v < 75) return 'Mixed Season'; return 'Altcoin Season'; } function fmtMC(n) { if (n >= 1e12) return '$' + (n/1e12).toFixed(2) + 'T'; if (n >= 1e9) return '$' + (n/1e9).toFixed(0) + 'B'; return '$' + (n/1e6).toFixed(0) + 'M'; } function initPulseStrip() { try { if (typeof MARKET_DATA === 'undefined') return; const d = MARKET_DATA; if (d.totalMarketCap) { document.getElementById('pulseMC').textContent = fmtMC(d.totalMarketCap); if (d.marketCapChange24h !== null && d.marketCapChange24h !== undefined) { const chg = d.marketCapChange24h; const sign = chg >= 0 ? '+' : ''; document.getElementById('pulseMCBadge').textContent = sign + chg.toFixed(2) + '% today'; document.getElementById('pulseMCBadge').className = 'pulse-badge ' + (chg >= 0 ? 'pb-greed' : 'pb-fear'); } else { document.getElementById('pulseMCBadge').textContent = 'Live'; document.getElementById('pulseMCBadge').className = 'pulse-badge pb-neutral'; } } if (d.btcDominance) { document.getElementById('pulseBTC').textContent = d.btcDominance.toFixed(1) + '%'; document.getElementById('pulseBTCBadge').textContent = 'of market'; document.getElementById('pulseBTCBadge').className = 'pulse-badge pb-btc'; } if (d.fearGreed !== undefined && d.fearGreed !== null) { const fg = d.fearGreed; let dirArrow = ''; if (d.fearGreedYesterday !== null && d.fearGreedYesterday !== undefined) { const diff = fg - d.fearGreedYesterday; if (diff > 0) dirArrow = ' ↑'; else if (diff < 0) dirArrow = ' ↓'; else dirArrow = ' →'; } document.getElementById('pulseFG').textContent = fg + dirArrow; document.getElementById('pulseFGBadge').textContent = d.fearGreedLabel || ''; document.getElementById('pulseFGBadge').className = 'pulse-badge ' + fgClass(fg); } } catch(e) { console.warn('Pulse strip error:', e); } } document.addEventListener('DOMContentLoaded', () => { const _months = ['January','February','March','April','May','June','July','August','September','October','November','December']; const today = localDateStr(new Date()); const body = document.body; const seoName = body.dataset.seoName || 'Bitcoin'; const seoLabel = body.dataset.seoLabel || '5 years ago'; // Inject coin name into hero sub-paragraph const heroSubCoin = document.getElementById('heroSubCoin'); if (heroSubCoin) heroSubCoin.textContent = seoName; let pageDate, pageDateFormatted, dpMinDate; if (body.dataset.seoDate) { // ABSOLUTE page — date is fixed (e.g. "2017-01-01") pageDate = body.dataset.seoDate; const d = new Date(pageDate + 'T00:00:00Z'); pageDateFormatted = d.getDate() + ' ' + _months[d.getMonth()] + ' ' + d.getFullYear(); dpMinDate = pageDate; } else { // RELATIVE page — parse N from seoLabel ("3 years ago" -> 3) const match = seoLabel.match(/^(\d+)/); const n = match ? parseInt(match[1]) : 5; const nYearsAgo = new Date(); nYearsAgo.setFullYear(nYearsAgo.getFullYear() - n); pageDate = localDateStr(nYearsAgo); pageDateFormatted = nYearsAgo.getDate() + ' ' + _months[nYearsAgo.getMonth()] + ' ' + nYearsAgo.getFullYear(); body.dataset.seoDate = pageDate; dpMinDate = pageDate; } // Inject the correct date into all placeholder spans ['heroDatePhrase','introDate','chartSinceDate','faqDate1','faqDate4'].forEach(id => { const el = document.getElementById(id); if (el) el.textContent = pageDateFormatted; }); dpInit(dpMinDate, today, dpMinDate); initPulseStrip(); loadSEOChart(); }); async function loadSEOChart() { try { const body = document.body; const seoCoin = body.dataset.seoCoin || 'BTC-USD'; const seoColor = body.dataset.seoColor || '#f7931a'; const seoName = body.dataset.seoName || 'Bitcoin'; const seoLabel = body.dataset.seoLabel || '5 years ago'; // Always fetch the full 5y dataset — Yahoo Finance range strings above 2y are unreliable. // We filter to the correct start date ourselves using the dynamically set data-seo-date. const allPrices = await fetchPrices(seoCoin, 'max'); if (!allPrices || allPrices.length < 2) return; // Get the start date from the body attribute (set dynamically by JS for relative pages) const startDate = body.dataset.seoDate || allPrices[0].date; const startIdx = startDate ? allPrices.findIndex(p => p.date >= startDate) : 0; const prices = allPrices.slice(startIdx >= 0 ? startIdx : 0); if (!prices || prices.length < 2) return; const coinsOwned = 1000 / prices[0].price; const values = prices.map(p => coinsOwned * p.price); const labels = prices.map(p => p.date); let peakValue = 0, peakDate = '', peakIdx = 0; values.forEach((v, i) => { if (v > peakValue) { peakValue = v; peakDate = labels[i]; peakIdx = i; } }); exampleChart = drawChart('exampleChart', labels, values, seoColor, peakIdx, peakValue, exampleChart); // ── Populate summary block ────────────────────────────────────────────── const currentValue = values[values.length - 1]; const profit = currentValue - 1000; const roi = (profit / 1000) * 100; const startDateFmt = fmtDate(body.dataset.seoDate || labels[0]); const profitClass = profit >= 0 ? 'pos' : 'neg'; const roiClass = roi >= 0 ? 'pos' : 'neg'; document.getElementById('seoHeadline').innerHTML = `If you invested $1,000 in ${seoName} on ${startDateFmt}, it would be worth ${fmt(currentValue)} today.`; document.getElementById('seoStatGrid').innerHTML = `
Invested
$1,000
Current Value
${fmt(currentValue)}
Profit / Loss
${fmt(profit)}
ROI
${roi >= 0 ? '+' : ''}${roi.toFixed(1)}%
Peak Value
${fmt(peakValue)}
Peak Date
${fmtDate(peakDate)}
`; document.getElementById('seoSummary').style.display = 'block'; document.getElementById('exampleChartTitle').textContent = seoName + ' — $1,000 invested ' + seoLabel; document.getElementById('peakTitle').textContent = seoName + ' — Peak profit moment'; document.getElementById('peakDesc').textContent = `If you had sold at the peak, your $1,000 would have been worth ${peakValue.toLocaleString('en-US', {maximumFractionDigits: 0})}`; document.getElementById('peakAmount').textContent = fmt(peakValue); document.getElementById('peakDate').textContent = fmtDate(peakDate); const faqPeakDate = document.getElementById('faqPeakDate'); const faqPeakValue = document.getElementById('faqPeakValue'); if (faqPeakDate) faqPeakDate.textContent = fmtDate(peakDate); if (faqPeakValue) faqPeakValue.textContent = fmt(peakValue); } catch(e) { console.warn('Could not load SEO chart:', e); } } // ─── CUSTOM DATE PICKER ─────────────────────────────────────────────────────── let _dpMin, _dpMax, _dpSel, _dpView = 'days'; let _dpCurYear, _dpCurMonth; function dpInit(min, max, defaultVal) { // Parse as local midnight to avoid UTC offset shifting the date by a day _dpMin = new Date(min + 'T00:00:00'); _dpMax = new Date(max + 'T00:00:00'); const d = new Date(defaultVal + 'T00:00:00'); _dpCurYear = d.getFullYear(); _dpCurMonth = d.getMonth(); dpSetDate(defaultVal); } function dpToggle() { const popup = document.getElementById('dpPopup'); const input = document.getElementById('dpInput'); const isOpen = popup.classList.contains('open'); if (isOpen) { popup.classList.remove('open'); input.classList.remove('open'); } else { popup.classList.add('open'); input.classList.add('open'); dpRender(); } } function dpClose() { document.getElementById('dpPopup').classList.remove('open'); document.getElementById('dpInput').classList.remove('open'); _dpView = 'days'; } document.addEventListener('click', e => { if (!document.getElementById('dpWrap').contains(e.target)) dpClose(); }); function dpNavMonth(dir) { if (_dpView === 'days') { _dpCurMonth += dir; if (_dpCurMonth > 11) { _dpCurMonth = 0; _dpCurYear++; } if (_dpCurMonth < 0) { _dpCurMonth = 11; _dpCurYear--; } } else if (_dpView === 'years') { _dpCurYear += dir * 12; } dpRender(); } function dpToggleView() { if (_dpView === 'days') _dpView = 'months'; else if (_dpView === 'months') _dpView = 'years'; else _dpView = 'days'; dpRender(); } function dpRender() { const MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December']; const MONTHS_SHORT = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; document.getElementById('dpMonthYear').textContent = MONTHS[_dpCurMonth] + ' ' + _dpCurYear; document.getElementById('dpDayView').style.display = _dpView === 'days' ? '' : 'none'; document.getElementById('dpMonthView').classList.toggle('open', _dpView === 'months'); document.getElementById('dpYearView').classList.toggle('open', _dpView === 'years'); if (_dpView === 'days') { const first = new Date(_dpCurYear, _dpCurMonth, 1).getDay(); const daysInMonth = new Date(_dpCurYear, _dpCurMonth + 1, 0).getDate(); let html = ''; for (let i = 0; i < first; i++) html += '
'; for (let d = 1; d <= daysInMonth; d++) { const ds = _dpCurYear + '-' + String(_dpCurMonth+1).padStart(2,'0') + '-' + String(d).padStart(2,'0'); const dt = new Date(ds + 'T00:00:00'); const today = localDateStr(new Date()); const isSel = ds === _dpSel; const isToday = ds === today; const isDisabled = dt < _dpMin || dt > _dpMax; html += `
${d}
`; } document.getElementById('dpDays').innerHTML = html; } else if (_dpView === 'months') { let html = ''; for (let m = 0; m < 12; m++) { const anyValid = !Array.from({length:new Date(_dpCurYear,m+1,0).getDate()},(_,i)=>{ const dt=new Date(_dpCurYear,m,i+1); return dt>=_dpMin&&dt<=_dpMax; }).every(v=>!v); const isSel = _dpSel && parseInt(_dpSel.slice(5,7))-1===m && parseInt(_dpSel.slice(0,4))===_dpCurYear; html += `
${MONTHS_SHORT[m]}
`; } document.getElementById('dpMonthView').innerHTML = html; } else { const base = Math.floor(_dpCurYear / 12) * 12; let html = ''; for (let y = base; y < base + 12; y++) { const anyValid = !(new Date(y,11,31) < _dpMin || new Date(y,0,1) > _dpMax); const isSel = _dpSel && parseInt(_dpSel.slice(0,4)) === y; html += `
${y}
`; } document.getElementById('dpYearView').innerHTML = html; } } function dpPickDay(ds) { dpSetDate(ds); dpClose(); } function dpPickMonth(m) { _dpCurMonth = m; _dpView = 'days'; dpRender(); } function dpPickYear(y) { _dpCurYear = y; _dpView = 'months'; dpRender(); } function dpSetDate(ds) { _dpSel = ds; document.getElementById('dateInput').value = ds; const d = new Date(ds + 'T00:00:00'); _dpCurYear = d.getFullYear(); _dpCurMonth = d.getMonth(); const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; const display = document.getElementById('dpDisplay'); display.textContent = d.getDate() + ' ' + MONTHS[d.getMonth()] + ' ' + d.getFullYear(); display.classList.remove('dp-placeholder'); } function dpSetQuick(yearsAgo) { const d = new Date(); d.setFullYear(d.getFullYear() - yearsAgo); // Clamp to min/max if (d < _dpMin) d.setTime(_dpMin.getTime()); if (d > _dpMax) d.setTime(_dpMax.getTime()); // Use local date string so NZ/UTC+12/+13 users don't get shifted to the wrong day dpSetDate(localDateStr(d)); dpClose(); } + n.toLocaleString('en-US', {maximumFractionDigits: 0}); return ' function fmtDate(s) { if (!s) return '—'; const d = new Date(s + 'T00:00:00Z'); return d.toLocaleDateString('en-US', {day: 'numeric', month: 'short', year: 'numeric', timeZone: 'UTC'}); } // ─── FETCH YAHOO FINANCE ────────────────────────────────────────────────────── async function fetchPrices(symbol, range='5y') { const key = symbol + range; if (priceCache[key]) return priceCache[key]; // Uses proxy.php on the same server to avoid CORS issues const url = `https://cryptoretail.store/proxy.php?symbol=${encodeURIComponent(symbol)}&range=${encodeURIComponent(range)}`; const res = await fetch(url); if (!res.ok) throw new Error('HTTP ' + res.status); const data = await res.json(); if (data.error) throw new Error(data.error); priceCache[key] = data.points; return data.points; } // ─── DRAW CHART ─────────────────────────────────────────────────────────────── function drawChart(canvasId, labels, values, color, peakIdx, peakVal, existingChart) { const ctx = document.getElementById(canvasId).getContext('2d'); if (existingChart) existingChart.destroy(); const h = canvasId === 'exampleChart' ? 320 : 280; const dataMax = Math.max(...values.filter(v => v !== null && v !== undefined)); const yMax = dataMax * 1.08; const grad = ctx.createLinearGradient(0, 0, 0, h); grad.addColorStop(0, color + '40'); grad.addColorStop(1, color + '00'); const annotations = {}; if (peakIdx !== undefined && peakVal !== undefined) { annotations['peakLine'] = { type: 'line', xMin: peakIdx, xMax: peakIdx, borderColor: 'rgba(63,185,80,0.65)', borderWidth: 2, borderDash: [4,4] }; } return new Chart(ctx, { type: 'line', data: { labels, datasets: [{ data: values, borderColor: color, backgroundColor: grad, borderWidth: 2, pointRadius: 0, pointHoverRadius: 5, fill: true, tension: 0.3 }, { // Peak dot — a single highlighted point at the peak index data: values.map((v, i) => i === peakIdx ? v : null), borderColor: '#3fb950', backgroundColor: '#3fb950', pointRadius: values.map((v, i) => i === peakIdx ? 7 : 0), pointHoverRadius: values.map((v, i) => i === peakIdx ? 9 : 0), borderWidth: 0, fill: false, tension: 0.3, pointStyle: 'circle', showLine: false }] }, options: { responsive: true, maintainAspectRatio: false, layout: { padding: { top: 15 } }, interaction: {mode: 'index', intersect: false}, plugins: { legend: {display: false}, tooltip: { backgroundColor: '#161b22', borderColor: '#30363d', borderWidth: 1, titleColor: '#8b949e', bodyColor: '#e6edf3', callbacks: { title: ctx => fmtDate(ctx[0].label), label: ctx => `Value: ${fmt(ctx.raw)}` } }, annotation: Object.keys(annotations).length ? {annotations} : {} }, scales: { x: { grid: {color: 'rgba(48,54,61,0.5)'}, ticks: { color: '#8b949e', maxTicksLimit: 7, callback: (val, idx, ticks) => { const lbl = ticks[idx]?.label; if (!lbl) return ''; return new Date(lbl + 'T00:00:00Z').getFullYear(); } } }, y: { grid: {color: 'rgba(48,54,61,0.5)'}, ticks: {color: '#8b949e', callback: v => fmt(v)}, max: yMax } } } }); } // ─── BUILD EXAMPLE CARDS ───────────────────────────────────────────────────── function buildCards() { const grid = document.getElementById('examplesGrid'); EXAMPLES.forEach((ex, i) => { const card = document.createElement('div'); card.className = 'example-card' + (i === 0 ? ' active' : ''); card.style.setProperty('--card-color', ex.color); card.id = 'card-' + i; card.onclick = () => selectExample(i); if (ex.isSP500) card.classList.add('sp-card'); card.innerHTML = ex.isSP500 ? `
${ex.icon}
${ex.name}
${ex.ticker}
Benchmark
$1,000 invested 5 years ago
Loading...
How does crypto compare to traditional markets?
` : `
${ex.icon}
${ex.name}
${ex.ticker}
$1,000 invested 5 years ago
Loading...
`; grid.appendChild(card); }); } // ─── LOAD ALL EXAMPLES ──────────────────────────────────────────────────────── async function loadExamples() { for (let i = 0; i < EXAMPLES.length; i++) { const ex = EXAMPLES[i]; try { const prices = await fetchPrices(ex.symbol, '5y'); if (!prices || prices.length < 2) continue; const coinsOwned = 1000 / prices[0].price; const currentValue = coinsOwned * prices[prices.length - 1].price; const roi = ((currentValue - 1000) / 1000) * 100; let peakValue = 0, peakDate = ''; prices.forEach(p => { const v = coinsOwned * p.price; if (v > peakValue) { peakValue = v; peakDate = p.date; } }); EXAMPLES[i] = {...ex, prices, coinsOwned, currentValue, roi, peakValue, peakDate}; document.getElementById('val-' + i).textContent = fmt(currentValue); const roiEl = document.getElementById('roi-' + i); roiEl.textContent = fmtPct(roi); roiEl.className = 'example-roi ' + (roi >= 0 ? 'roi-positive' : 'roi-negative'); if (!ex.isSP500) { const peakRow = document.getElementById('peak-row-' + i); if (peakRow) { peakRow.style.display = 'block'; document.getElementById('peak-val-' + i).textContent = fmt(peakValue); document.getElementById('peak-dt-' + i).textContent = fmtDate(peakDate); } } if (i === 0) selectExample(0); } catch(e) { document.getElementById('val-' + i).textContent = 'Unavailable'; } } } // ─── SELECT EXAMPLE ─────────────────────────────────────────────────────────── function selectExample(i) { activeIdx = i; document.querySelectorAll('.example-card').forEach((c, j) => c.classList.toggle('active', j === i)); const ex = EXAMPLES[i]; if (!ex.prices) return; document.getElementById('exampleChartTitle').textContent = `${ex.name} — $1,000 invested 5 years ago`; const labels = ex.prices.map(p => p.date); const values = ex.prices.map(p => ex.coinsOwned * p.price); const peakIdx = values.indexOf(Math.max(...values)); exampleChart = drawChart('exampleChart', labels, values, ex.color, peakIdx, ex.peakValue, exampleChart); document.getElementById('peakTitle').textContent = `${ex.name} — Peak profit moment`; document.getElementById('peakDesc').textContent = `If you had sold at the peak, your $1,000 would have been worth ${fmt(ex.peakValue)}`; document.getElementById('peakAmount').textContent = fmt(ex.peakValue); document.getElementById('peakDate').textContent = fmtDate(ex.peakDate); } // ─── CUSTOM CALCULATOR ──────────────────────────────────────────────────────── async function runCalculation() { const symbol = document.getElementById('coinSelect').value; const coinName = document.getElementById('coinSelect').selectedOptions[0].dataset.name; const amount = parseFloat(document.getElementById('amountInput').value); const date = document.getElementById('dateInput').value; if (!amount || amount <= 0) { showErr('Please enter a valid investment amount.'); return; } if (!date) { showErr('Please select a purchase date.'); return; } const today = localDateStr(new Date()); // Allow up to 5 years + 3 days to account for any timezone offset worldwide const minDate = new Date(); minDate.setFullYear(minDate.getFullYear() - 5); minDate.setDate(minDate.getDate() - 3); if (date > today) { showErr('Purchase date cannot be in the future.'); return; } if (date < localDateStr(minDate)) { showErr('Please select a date within the last 5 years.'); return; } document.getElementById('calcLoading').style.display = 'block'; document.getElementById('resultBox').style.display = 'none'; document.getElementById('calcError').style.display = 'none'; document.getElementById('calcBtn').disabled = true; try { const prices = await fetchPrices(symbol, '5y'); const afterDate = prices.filter(p => p.date >= date); if (!afterDate.length) { showErr('No price data available for this date. Try a more recent date.'); return; } const buyPt = afterDate[0]; const curPt = prices[prices.length - 1]; const coinsOwned = amount / buyPt.price; const currentValue = coinsOwned * curPt.price; const profit = currentValue - amount; const roi = (profit / amount) * 100; const relevant = prices.filter(p => p.date >= buyPt.date); const labels = relevant.map(p => p.date); const values = relevant.map(p => coinsOwned * p.price); let peakValue = 0, peakDate = '', peakIdx = 0; values.forEach((v, i) => { if (v > peakValue) { peakValue = v; peakDate = labels[i]; peakIdx = i; } }); document.getElementById('resultHeadline').innerHTML = `If you invested ${fmt(amount)} in ${coinName} on ${fmtDate(date)}, it would be worth ${fmt(currentValue)} today.`; document.getElementById('resultStats').innerHTML = `
Invested
${fmt(amount)}
Current Value
${fmt(currentValue)}
Profit / Loss
${fmt(profit)}
ROI
${fmtPct(roi)}
Peak Value
${fmt(peakValue)}
Peak Date
${fmtDate(peakDate)}
`; const color = EXAMPLES.find(e => e.symbol === symbol)?.color || '#f7931a'; customChart = drawChart('customChart', labels, values, color, peakIdx, peakValue, customChart); window._lastResult = {coinName, amount, date, currentValue, profit, roi, peakValue, peakDate}; document.getElementById('resultBox').style.display = 'block'; document.getElementById('resultBox').scrollIntoView({behavior:'smooth', block:'start'}); } catch(e) { showErr('Could not fetch price data. Please try again or select a different coin or date.'); console.error(e); } finally { document.getElementById('calcLoading').style.display = 'none'; document.getElementById('calcBtn').disabled = false; } } function showErr(msg) { document.getElementById('calcLoading').style.display = 'none'; document.getElementById('calcBtn').disabled = false; const el = document.getElementById('calcError'); el.textContent = msg; el.style.display = 'block'; } // ─── SHARE ──────────────────────────────────────────────────────────────────── function copyResult() { const r = window._lastResult; if (!r) return; const t = `If I had invested ${fmt(r.amount)} in ${r.coinName} on ${fmtDate(r.date)}, it would be worth ${fmt(r.currentValue)} today (${fmtPct(r.roi)} ROI). Peak was ${fmt(r.peakValue)} on ${fmtDate(r.peakDate)}. Calculate yours at cryptoretail.store`; navigator.clipboard.writeText(t).then(() => alert('Copied to clipboard!')); } function shareOnX() { const r = window._lastResult; if (!r) return; const t = `If I invested ${fmt(r.amount)} in ${r.coinName} on ${fmtDate(r.date)}, it would be worth ${fmt(r.currentValue)} today (${fmtPct(r.roi)} ROI) 🚀\n\nCalculate yours 👇`; window.open(`https://twitter.com/intent/tweet?text=${encodeURIComponent(t)}&url=${encodeURIComponent('https://cryptoretail.store')}`, '_blank'); } // ─── INIT ───────────────────────────────────────────────────────────────────── // Returns YYYY-MM-DD in the user's LOCAL timezone (avoids UTC-offset boundary issues) function localDateStr(d) { const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); return `${y}-${m}-${day}`; } // ─── MARKET PULSE STRIP ─────────────────────────────────────────────────────────────── function fgClass(v) { if (v < 20) return 'pb-efear'; if (v < 40) return 'pb-fear'; if (v < 60) return 'pb-neutral'; if (v < 80) return 'pb-greed'; return 'pb-egreed'; } function asiClass(v) { if (v < 25) return 'pb-fear'; if (v < 75) return 'pb-neutral'; return 'pb-greed'; } function asiLabel(v) { if (v < 25) return 'Bitcoin Season'; if (v < 75) return 'Mixed Season'; return 'Altcoin Season'; } function fmtMC(n) { if (n >= 1e12) return '$' + (n/1e12).toFixed(2) + 'T'; if (n >= 1e9) return '$' + (n/1e9).toFixed(0) + 'B'; return '$' + (n/1e6).toFixed(0) + 'M'; } function initPulseStrip() { try { if (typeof MARKET_DATA === 'undefined') return; const d = MARKET_DATA; if (d.totalMarketCap) { document.getElementById('pulseMC').textContent = fmtMC(d.totalMarketCap); if (d.marketCapChange24h !== null && d.marketCapChange24h !== undefined) { const chg = d.marketCapChange24h; const sign = chg >= 0 ? '+' : ''; document.getElementById('pulseMCBadge').textContent = sign + chg.toFixed(2) + '% today'; document.getElementById('pulseMCBadge').className = 'pulse-badge ' + (chg >= 0 ? 'pb-greed' : 'pb-fear'); } else { document.getElementById('pulseMCBadge').textContent = 'Live'; document.getElementById('pulseMCBadge').className = 'pulse-badge pb-neutral'; } } if (d.btcDominance) { document.getElementById('pulseBTC').textContent = d.btcDominance.toFixed(1) + '%'; document.getElementById('pulseBTCBadge').textContent = 'of market'; document.getElementById('pulseBTCBadge').className = 'pulse-badge pb-btc'; } if (d.fearGreed !== undefined && d.fearGreed !== null) { const fg = d.fearGreed; let dirArrow = ''; if (d.fearGreedYesterday !== null && d.fearGreedYesterday !== undefined) { const diff = fg - d.fearGreedYesterday; if (diff > 0) dirArrow = ' ↑'; else if (diff < 0) dirArrow = ' ↓'; else dirArrow = ' →'; } document.getElementById('pulseFG').textContent = fg + dirArrow; document.getElementById('pulseFGBadge').textContent = d.fearGreedLabel || ''; document.getElementById('pulseFGBadge').className = 'pulse-badge ' + fgClass(fg); } } catch(e) { console.warn('Pulse strip error:', e); } } document.addEventListener('DOMContentLoaded', () => { const _months = ['January','February','March','April','May','June','July','August','September','October','November','December']; const today = localDateStr(new Date()); const body = document.body; const seoName = body.dataset.seoName || 'Bitcoin'; const seoLabel = body.dataset.seoLabel || '5 years ago'; // Inject coin name into hero sub-paragraph const heroSubCoin = document.getElementById('heroSubCoin'); if (heroSubCoin) heroSubCoin.textContent = seoName; let pageDate, pageDateFormatted, dpMinDate; if (body.dataset.seoDate) { // ABSOLUTE page — date is fixed (e.g. "2017-01-01") pageDate = body.dataset.seoDate; const d = new Date(pageDate + 'T00:00:00Z'); pageDateFormatted = d.getDate() + ' ' + _months[d.getMonth()] + ' ' + d.getFullYear(); dpMinDate = pageDate; } else { // RELATIVE page — parse N from seoLabel ("3 years ago" -> 3) const match = seoLabel.match(/^(\d+)/); const n = match ? parseInt(match[1]) : 5; const nYearsAgo = new Date(); nYearsAgo.setFullYear(nYearsAgo.getFullYear() - n); pageDate = localDateStr(nYearsAgo); pageDateFormatted = nYearsAgo.getDate() + ' ' + _months[nYearsAgo.getMonth()] + ' ' + nYearsAgo.getFullYear(); body.dataset.seoDate = pageDate; dpMinDate = pageDate; } // Inject the correct date into all placeholder spans ['heroDatePhrase','introDate','chartSinceDate','faqDate1','faqDate4'].forEach(id => { const el = document.getElementById(id); if (el) el.textContent = pageDateFormatted; }); dpInit(dpMinDate, today, dpMinDate); initPulseStrip(); loadSEOChart(); }); async function loadSEOChart() { try { const body = document.body; const seoCoin = body.dataset.seoCoin || 'BTC-USD'; const seoColor = body.dataset.seoColor || '#f7931a'; const seoName = body.dataset.seoName || 'Bitcoin'; const seoLabel = body.dataset.seoLabel || '5 years ago'; // Always fetch the full 5y dataset — Yahoo Finance range strings above 2y are unreliable. // We filter to the correct start date ourselves using the dynamically set data-seo-date. const allPrices = await fetchPrices(seoCoin, 'max'); if (!allPrices || allPrices.length < 2) return; // Get the start date from the body attribute (set dynamically by JS for relative pages) const startDate = body.dataset.seoDate || allPrices[0].date; const startIdx = startDate ? allPrices.findIndex(p => p.date >= startDate) : 0; const prices = allPrices.slice(startIdx >= 0 ? startIdx : 0); if (!prices || prices.length < 2) return; const coinsOwned = 1000 / prices[0].price; const values = prices.map(p => coinsOwned * p.price); const labels = prices.map(p => p.date); let peakValue = 0, peakDate = '', peakIdx = 0; values.forEach((v, i) => { if (v > peakValue) { peakValue = v; peakDate = labels[i]; peakIdx = i; } }); exampleChart = drawChart('exampleChart', labels, values, seoColor, peakIdx, peakValue, exampleChart); // ── Populate summary block ────────────────────────────────────────────── const currentValue = values[values.length - 1]; const profit = currentValue - 1000; const roi = (profit / 1000) * 100; const startDateFmt = fmtDate(body.dataset.seoDate || labels[0]); const profitClass = profit >= 0 ? 'pos' : 'neg'; const roiClass = roi >= 0 ? 'pos' : 'neg'; document.getElementById('seoHeadline').innerHTML = `If you invested $1,000 in ${seoName} on ${startDateFmt}, it would be worth ${fmt(currentValue)} today.`; document.getElementById('seoStatGrid').innerHTML = `
Invested
$1,000
Current Value
${fmt(currentValue)}
Profit / Loss
${fmt(profit)}
ROI
${roi >= 0 ? '+' : ''}${roi.toFixed(1)}%
Peak Value
${fmt(peakValue)}
Peak Date
${fmtDate(peakDate)}
`; document.getElementById('seoSummary').style.display = 'block'; document.getElementById('exampleChartTitle').textContent = seoName + ' — $1,000 invested ' + seoLabel; document.getElementById('peakTitle').textContent = seoName + ' — Peak profit moment'; document.getElementById('peakDesc').textContent = `If you had sold at the peak, your $1,000 would have been worth ${peakValue.toLocaleString('en-US', {maximumFractionDigits: 0})}`; document.getElementById('peakAmount').textContent = fmt(peakValue); document.getElementById('peakDate').textContent = fmtDate(peakDate); const faqPeakDate = document.getElementById('faqPeakDate'); const faqPeakValue = document.getElementById('faqPeakValue'); if (faqPeakDate) faqPeakDate.textContent = fmtDate(peakDate); if (faqPeakValue) faqPeakValue.textContent = fmt(peakValue); } catch(e) { console.warn('Could not load SEO chart:', e); } } // ─── CUSTOM DATE PICKER ─────────────────────────────────────────────────────── let _dpMin, _dpMax, _dpSel, _dpView = 'days'; let _dpCurYear, _dpCurMonth; function dpInit(min, max, defaultVal) { // Parse as local midnight to avoid UTC offset shifting the date by a day _dpMin = new Date(min + 'T00:00:00'); _dpMax = new Date(max + 'T00:00:00'); const d = new Date(defaultVal + 'T00:00:00'); _dpCurYear = d.getFullYear(); _dpCurMonth = d.getMonth(); dpSetDate(defaultVal); } function dpToggle() { const popup = document.getElementById('dpPopup'); const input = document.getElementById('dpInput'); const isOpen = popup.classList.contains('open'); if (isOpen) { popup.classList.remove('open'); input.classList.remove('open'); } else { popup.classList.add('open'); input.classList.add('open'); dpRender(); } } function dpClose() { document.getElementById('dpPopup').classList.remove('open'); document.getElementById('dpInput').classList.remove('open'); _dpView = 'days'; } document.addEventListener('click', e => { if (!document.getElementById('dpWrap').contains(e.target)) dpClose(); }); function dpNavMonth(dir) { if (_dpView === 'days') { _dpCurMonth += dir; if (_dpCurMonth > 11) { _dpCurMonth = 0; _dpCurYear++; } if (_dpCurMonth < 0) { _dpCurMonth = 11; _dpCurYear--; } } else if (_dpView === 'years') { _dpCurYear += dir * 12; } dpRender(); } function dpToggleView() { if (_dpView === 'days') _dpView = 'months'; else if (_dpView === 'months') _dpView = 'years'; else _dpView = 'days'; dpRender(); } function dpRender() { const MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December']; const MONTHS_SHORT = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; document.getElementById('dpMonthYear').textContent = MONTHS[_dpCurMonth] + ' ' + _dpCurYear; document.getElementById('dpDayView').style.display = _dpView === 'days' ? '' : 'none'; document.getElementById('dpMonthView').classList.toggle('open', _dpView === 'months'); document.getElementById('dpYearView').classList.toggle('open', _dpView === 'years'); if (_dpView === 'days') { const first = new Date(_dpCurYear, _dpCurMonth, 1).getDay(); const daysInMonth = new Date(_dpCurYear, _dpCurMonth + 1, 0).getDate(); let html = ''; for (let i = 0; i < first; i++) html += '
'; for (let d = 1; d <= daysInMonth; d++) { const ds = _dpCurYear + '-' + String(_dpCurMonth+1).padStart(2,'0') + '-' + String(d).padStart(2,'0'); const dt = new Date(ds + 'T00:00:00'); const today = localDateStr(new Date()); const isSel = ds === _dpSel; const isToday = ds === today; const isDisabled = dt < _dpMin || dt > _dpMax; html += `
${d}
`; } document.getElementById('dpDays').innerHTML = html; } else if (_dpView === 'months') { let html = ''; for (let m = 0; m < 12; m++) { const anyValid = !Array.from({length:new Date(_dpCurYear,m+1,0).getDate()},(_,i)=>{ const dt=new Date(_dpCurYear,m,i+1); return dt>=_dpMin&&dt<=_dpMax; }).every(v=>!v); const isSel = _dpSel && parseInt(_dpSel.slice(5,7))-1===m && parseInt(_dpSel.slice(0,4))===_dpCurYear; html += `
${MONTHS_SHORT[m]}
`; } document.getElementById('dpMonthView').innerHTML = html; } else { const base = Math.floor(_dpCurYear / 12) * 12; let html = ''; for (let y = base; y < base + 12; y++) { const anyValid = !(new Date(y,11,31) < _dpMin || new Date(y,0,1) > _dpMax); const isSel = _dpSel && parseInt(_dpSel.slice(0,4)) === y; html += `
${y}
`; } document.getElementById('dpYearView').innerHTML = html; } } function dpPickDay(ds) { dpSetDate(ds); dpClose(); } function dpPickMonth(m) { _dpCurMonth = m; _dpView = 'days'; dpRender(); } function dpPickYear(y) { _dpCurYear = y; _dpView = 'months'; dpRender(); } function dpSetDate(ds) { _dpSel = ds; document.getElementById('dateInput').value = ds; const d = new Date(ds + 'T00:00:00'); _dpCurYear = d.getFullYear(); _dpCurMonth = d.getMonth(); const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; const display = document.getElementById('dpDisplay'); display.textContent = d.getDate() + ' ' + MONTHS[d.getMonth()] + ' ' + d.getFullYear(); display.classList.remove('dp-placeholder'); } function dpSetQuick(yearsAgo) { const d = new Date(); d.setFullYear(d.getFullYear() - yearsAgo); // Clamp to min/max if (d < _dpMin) d.setTime(_dpMin.getTime()); if (d > _dpMax) d.setTime(_dpMax.getTime()); // Use local date string so NZ/UTC+12/+13 users don't get shifted to the wrong day dpSetDate(localDateStr(d)); dpClose(); } + n.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2}); } function fmtPct(n) { if (!Number.isFinite(n)) return '—'; return (n >= 0 ? '+' : '') + n.toFixed(1) + '%'; } function fmtDate(s) { if (!s) return '—'; const d = new Date(s + 'T00:00:00Z'); return d.toLocaleDateString('en-US', {day: 'numeric', month: 'short', year: 'numeric', timeZone: 'UTC'}); } // ─── FETCH YAHOO FINANCE ────────────────────────────────────────────────────── async function fetchPrices(symbol, range='5y') { const key = symbol + range; if (priceCache[key]) return priceCache[key]; // Uses proxy.php on the same server to avoid CORS issues const url = `https://cryptoretail.store/proxy.php?symbol=${encodeURIComponent(symbol)}&range=${encodeURIComponent(range)}`; const res = await fetch(url); if (!res.ok) throw new Error('HTTP ' + res.status); const data = await res.json(); if (data.error) throw new Error(data.error); priceCache[key] = data.points; return data.points; } // ─── DRAW CHART ─────────────────────────────────────────────────────────────── function drawChart(canvasId, labels, values, color, peakIdx, peakVal, existingChart) { const ctx = document.getElementById(canvasId).getContext('2d'); if (existingChart) existingChart.destroy(); const h = canvasId === 'exampleChart' ? 320 : 280; const dataMax = Math.max(...values.filter(v => v !== null && v !== undefined)); const yMax = dataMax * 1.08; const grad = ctx.createLinearGradient(0, 0, 0, h); grad.addColorStop(0, color + '40'); grad.addColorStop(1, color + '00'); const annotations = {}; if (peakIdx !== undefined && peakVal !== undefined) { annotations['peakLine'] = { type: 'line', xMin: peakIdx, xMax: peakIdx, borderColor: 'rgba(63,185,80,0.65)', borderWidth: 2, borderDash: [4,4] }; } return new Chart(ctx, { type: 'line', data: { labels, datasets: [{ data: values, borderColor: color, backgroundColor: grad, borderWidth: 2, pointRadius: 0, pointHoverRadius: 5, fill: true, tension: 0.3 }, { // Peak dot — a single highlighted point at the peak index data: values.map((v, i) => i === peakIdx ? v : null), borderColor: '#3fb950', backgroundColor: '#3fb950', pointRadius: values.map((v, i) => i === peakIdx ? 7 : 0), pointHoverRadius: values.map((v, i) => i === peakIdx ? 9 : 0), borderWidth: 0, fill: false, tension: 0.3, pointStyle: 'circle', showLine: false }] }, options: { responsive: true, maintainAspectRatio: false, layout: { padding: { top: 15 } }, interaction: {mode: 'index', intersect: false}, plugins: { legend: {display: false}, tooltip: { backgroundColor: '#161b22', borderColor: '#30363d', borderWidth: 1, titleColor: '#8b949e', bodyColor: '#e6edf3', callbacks: { title: ctx => fmtDate(ctx[0].label), label: ctx => `Value: ${fmt(ctx.raw)}` } }, annotation: Object.keys(annotations).length ? {annotations} : {} }, scales: { x: { grid: {color: 'rgba(48,54,61,0.5)'}, ticks: { color: '#8b949e', maxTicksLimit: 7, callback: (val, idx, ticks) => { const lbl = ticks[idx]?.label; if (!lbl) return ''; return new Date(lbl + 'T00:00:00Z').getFullYear(); } } }, y: { grid: {color: 'rgba(48,54,61,0.5)'}, ticks: {color: '#8b949e', callback: v => fmt(v)}, max: yMax } } } }); } // ─── BUILD EXAMPLE CARDS ───────────────────────────────────────────────────── function buildCards() { const grid = document.getElementById('examplesGrid'); EXAMPLES.forEach((ex, i) => { const card = document.createElement('div'); card.className = 'example-card' + (i === 0 ? ' active' : ''); card.style.setProperty('--card-color', ex.color); card.id = 'card-' + i; card.onclick = () => selectExample(i); if (ex.isSP500) card.classList.add('sp-card'); card.innerHTML = ex.isSP500 ? `
${ex.icon}
${ex.name}
${ex.ticker}
Benchmark
$1,000 invested 5 years ago
Loading...
How does crypto compare to traditional markets?
` : `
${ex.icon}
${ex.name}
${ex.ticker}
$1,000 invested 5 years ago
Loading...
`; grid.appendChild(card); }); } // ─── LOAD ALL EXAMPLES ──────────────────────────────────────────────────────── async function loadExamples() { for (let i = 0; i < EXAMPLES.length; i++) { const ex = EXAMPLES[i]; try { const prices = await fetchPrices(ex.symbol, '5y'); if (!prices || prices.length < 2) continue; const coinsOwned = 1000 / prices[0].price; const currentValue = coinsOwned * prices[prices.length - 1].price; const roi = ((currentValue - 1000) / 1000) * 100; let peakValue = 0, peakDate = ''; prices.forEach(p => { const v = coinsOwned * p.price; if (v > peakValue) { peakValue = v; peakDate = p.date; } }); EXAMPLES[i] = {...ex, prices, coinsOwned, currentValue, roi, peakValue, peakDate}; document.getElementById('val-' + i).textContent = fmt(currentValue); const roiEl = document.getElementById('roi-' + i); roiEl.textContent = fmtPct(roi); roiEl.className = 'example-roi ' + (roi >= 0 ? 'roi-positive' : 'roi-negative'); if (!ex.isSP500) { const peakRow = document.getElementById('peak-row-' + i); if (peakRow) { peakRow.style.display = 'block'; document.getElementById('peak-val-' + i).textContent = fmt(peakValue); document.getElementById('peak-dt-' + i).textContent = fmtDate(peakDate); } } if (i === 0) selectExample(0); } catch(e) { document.getElementById('val-' + i).textContent = 'Unavailable'; } } } // ─── SELECT EXAMPLE ─────────────────────────────────────────────────────────── function selectExample(i) { activeIdx = i; document.querySelectorAll('.example-card').forEach((c, j) => c.classList.toggle('active', j === i)); const ex = EXAMPLES[i]; if (!ex.prices) return; document.getElementById('exampleChartTitle').textContent = `${ex.name} — $1,000 invested 5 years ago`; const labels = ex.prices.map(p => p.date); const values = ex.prices.map(p => ex.coinsOwned * p.price); const peakIdx = values.indexOf(Math.max(...values)); exampleChart = drawChart('exampleChart', labels, values, ex.color, peakIdx, ex.peakValue, exampleChart); document.getElementById('peakTitle').textContent = `${ex.name} — Peak profit moment`; document.getElementById('peakDesc').textContent = `If you had sold at the peak, your $1,000 would have been worth ${fmt(ex.peakValue)}`; document.getElementById('peakAmount').textContent = fmt(ex.peakValue); document.getElementById('peakDate').textContent = fmtDate(ex.peakDate); } // ─── CUSTOM CALCULATOR ──────────────────────────────────────────────────────── async function runCalculation() { const symbol = document.getElementById('coinSelect').value; const coinName = document.getElementById('coinSelect').selectedOptions[0].dataset.name; const amount = parseFloat(document.getElementById('amountInput').value); const date = document.getElementById('dateInput').value; if (!amount || amount <= 0) { showErr('Please enter a valid investment amount.'); return; } if (!date) { showErr('Please select a purchase date.'); return; } const today = localDateStr(new Date()); // Allow up to 5 years + 3 days to account for any timezone offset worldwide const minDate = new Date(); minDate.setFullYear(minDate.getFullYear() - 5); minDate.setDate(minDate.getDate() - 3); if (date > today) { showErr('Purchase date cannot be in the future.'); return; } if (date < localDateStr(minDate)) { showErr('Please select a date within the last 5 years.'); return; } document.getElementById('calcLoading').style.display = 'block'; document.getElementById('resultBox').style.display = 'none'; document.getElementById('calcError').style.display = 'none'; document.getElementById('calcBtn').disabled = true; try { const prices = await fetchPrices(symbol, '5y'); const afterDate = prices.filter(p => p.date >= date); if (!afterDate.length) { showErr('No price data available for this date. Try a more recent date.'); return; } const buyPt = afterDate[0]; const curPt = prices[prices.length - 1]; const coinsOwned = amount / buyPt.price; const currentValue = coinsOwned * curPt.price; const profit = currentValue - amount; const roi = (profit / amount) * 100; const relevant = prices.filter(p => p.date >= buyPt.date); const labels = relevant.map(p => p.date); const values = relevant.map(p => coinsOwned * p.price); let peakValue = 0, peakDate = '', peakIdx = 0; values.forEach((v, i) => { if (v > peakValue) { peakValue = v; peakDate = labels[i]; peakIdx = i; } }); document.getElementById('resultHeadline').innerHTML = `If you invested ${fmt(amount)} in ${coinName} on ${fmtDate(date)}, it would be worth ${fmt(currentValue)} today.`; document.getElementById('resultStats').innerHTML = `
Invested
${fmt(amount)}
Current Value
${fmt(currentValue)}
Profit / Loss
${fmt(profit)}
ROI
${fmtPct(roi)}
Peak Value
${fmt(peakValue)}
Peak Date
${fmtDate(peakDate)}
`; const color = EXAMPLES.find(e => e.symbol === symbol)?.color || '#f7931a'; customChart = drawChart('customChart', labels, values, color, peakIdx, peakValue, customChart); window._lastResult = {coinName, amount, date, currentValue, profit, roi, peakValue, peakDate}; document.getElementById('resultBox').style.display = 'block'; document.getElementById('resultBox').scrollIntoView({behavior:'smooth', block:'start'}); } catch(e) { showErr('Could not fetch price data. Please try again or select a different coin or date.'); console.error(e); } finally { document.getElementById('calcLoading').style.display = 'none'; document.getElementById('calcBtn').disabled = false; } } function showErr(msg) { document.getElementById('calcLoading').style.display = 'none'; document.getElementById('calcBtn').disabled = false; const el = document.getElementById('calcError'); el.textContent = msg; el.style.display = 'block'; } // ─── SHARE ──────────────────────────────────────────────────────────────────── function copyResult() { const r = window._lastResult; if (!r) return; const t = `If I had invested ${fmt(r.amount)} in ${r.coinName} on ${fmtDate(r.date)}, it would be worth ${fmt(r.currentValue)} today (${fmtPct(r.roi)} ROI). Peak was ${fmt(r.peakValue)} on ${fmtDate(r.peakDate)}. Calculate yours at cryptoretail.store`; navigator.clipboard.writeText(t).then(() => alert('Copied to clipboard!')); } function shareOnX() { const r = window._lastResult; if (!r) return; const t = `If I invested ${fmt(r.amount)} in ${r.coinName} on ${fmtDate(r.date)}, it would be worth ${fmt(r.currentValue)} today (${fmtPct(r.roi)} ROI) 🚀\n\nCalculate yours 👇`; window.open(`https://twitter.com/intent/tweet?text=${encodeURIComponent(t)}&url=${encodeURIComponent('https://cryptoretail.store')}`, '_blank'); } // ─── INIT ───────────────────────────────────────────────────────────────────── // Returns YYYY-MM-DD in the user's LOCAL timezone (avoids UTC-offset boundary issues) function localDateStr(d) { const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); return `${y}-${m}-${day}`; } // ─── MARKET PULSE STRIP ─────────────────────────────────────────────────────────────── function fgClass(v) { if (v < 20) return 'pb-efear'; if (v < 40) return 'pb-fear'; if (v < 60) return 'pb-neutral'; if (v < 80) return 'pb-greed'; return 'pb-egreed'; } function asiClass(v) { if (v < 25) return 'pb-fear'; if (v < 75) return 'pb-neutral'; return 'pb-greed'; } function asiLabel(v) { if (v < 25) return 'Bitcoin Season'; if (v < 75) return 'Mixed Season'; return 'Altcoin Season'; } function fmtMC(n) { if (n >= 1e12) return '$' + (n/1e12).toFixed(2) + 'T'; if (n >= 1e9) return '$' + (n/1e9).toFixed(0) + 'B'; return '$' + (n/1e6).toFixed(0) + 'M'; } function initPulseStrip() { try { if (typeof MARKET_DATA === 'undefined') return; const d = MARKET_DATA; if (d.totalMarketCap) { document.getElementById('pulseMC').textContent = fmtMC(d.totalMarketCap); if (d.marketCapChange24h !== null && d.marketCapChange24h !== undefined) { const chg = d.marketCapChange24h; const sign = chg >= 0 ? '+' : ''; document.getElementById('pulseMCBadge').textContent = sign + chg.toFixed(2) + '% today'; document.getElementById('pulseMCBadge').className = 'pulse-badge ' + (chg >= 0 ? 'pb-greed' : 'pb-fear'); } else { document.getElementById('pulseMCBadge').textContent = 'Live'; document.getElementById('pulseMCBadge').className = 'pulse-badge pb-neutral'; } } if (d.btcDominance) { document.getElementById('pulseBTC').textContent = d.btcDominance.toFixed(1) + '%'; document.getElementById('pulseBTCBadge').textContent = 'of market'; document.getElementById('pulseBTCBadge').className = 'pulse-badge pb-btc'; } if (d.fearGreed !== undefined && d.fearGreed !== null) { const fg = d.fearGreed; let dirArrow = ''; if (d.fearGreedYesterday !== null && d.fearGreedYesterday !== undefined) { const diff = fg - d.fearGreedYesterday; if (diff > 0) dirArrow = ' ↑'; else if (diff < 0) dirArrow = ' ↓'; else dirArrow = ' →'; } document.getElementById('pulseFG').textContent = fg + dirArrow; document.getElementById('pulseFGBadge').textContent = d.fearGreedLabel || ''; document.getElementById('pulseFGBadge').className = 'pulse-badge ' + fgClass(fg); } } catch(e) { console.warn('Pulse strip error:', e); } } document.addEventListener('DOMContentLoaded', () => { const _months = ['January','February','March','April','May','June','July','August','September','October','November','December']; const today = localDateStr(new Date()); const body = document.body; const seoName = body.dataset.seoName || 'Bitcoin'; const seoLabel = body.dataset.seoLabel || '5 years ago'; // Inject coin name into hero sub-paragraph const heroSubCoin = document.getElementById('heroSubCoin'); if (heroSubCoin) heroSubCoin.textContent = seoName; let pageDate, pageDateFormatted, dpMinDate; if (body.dataset.seoDate) { // ABSOLUTE page — date is fixed (e.g. "2017-01-01") pageDate = body.dataset.seoDate; const d = new Date(pageDate + 'T00:00:00Z'); pageDateFormatted = d.getDate() + ' ' + _months[d.getMonth()] + ' ' + d.getFullYear(); dpMinDate = pageDate; } else { // RELATIVE page — parse N from seoLabel ("3 years ago" -> 3) const match = seoLabel.match(/^(\d+)/); const n = match ? parseInt(match[1]) : 5; const nYearsAgo = new Date(); nYearsAgo.setFullYear(nYearsAgo.getFullYear() - n); pageDate = localDateStr(nYearsAgo); pageDateFormatted = nYearsAgo.getDate() + ' ' + _months[nYearsAgo.getMonth()] + ' ' + nYearsAgo.getFullYear(); body.dataset.seoDate = pageDate; dpMinDate = pageDate; } // Inject the correct date into all placeholder spans ['heroDatePhrase','introDate','chartSinceDate','faqDate1','faqDate4'].forEach(id => { const el = document.getElementById(id); if (el) el.textContent = pageDateFormatted; }); dpInit(dpMinDate, today, dpMinDate); initPulseStrip(); loadSEOChart(); }); async function loadSEOChart() { try { const body = document.body; const seoCoin = body.dataset.seoCoin || 'BTC-USD'; const seoColor = body.dataset.seoColor || '#f7931a'; const seoName = body.dataset.seoName || 'Bitcoin'; const seoLabel = body.dataset.seoLabel || '5 years ago'; // Always fetch the full 5y dataset — Yahoo Finance range strings above 2y are unreliable. // We filter to the correct start date ourselves using the dynamically set data-seo-date. const allPrices = await fetchPrices(seoCoin, 'max'); if (!allPrices || allPrices.length < 2) return; // Get the start date from the body attribute (set dynamically by JS for relative pages) const startDate = body.dataset.seoDate || allPrices[0].date; const startIdx = startDate ? allPrices.findIndex(p => p.date >= startDate) : 0; const prices = allPrices.slice(startIdx >= 0 ? startIdx : 0); if (!prices || prices.length < 2) return; const coinsOwned = 1000 / prices[0].price; const values = prices.map(p => coinsOwned * p.price); const labels = prices.map(p => p.date); let peakValue = 0, peakDate = '', peakIdx = 0; values.forEach((v, i) => { if (v > peakValue) { peakValue = v; peakDate = labels[i]; peakIdx = i; } }); exampleChart = drawChart('exampleChart', labels, values, seoColor, peakIdx, peakValue, exampleChart); // ── Populate summary block ────────────────────────────────────────────── const currentValue = values[values.length - 1]; const profit = currentValue - 1000; const roi = (profit / 1000) * 100; const startDateFmt = fmtDate(body.dataset.seoDate || labels[0]); const profitClass = profit >= 0 ? 'pos' : 'neg'; const roiClass = roi >= 0 ? 'pos' : 'neg'; document.getElementById('seoHeadline').innerHTML = `If you invested $1,000 in ${seoName} on ${startDateFmt}, it would be worth ${fmt(currentValue)} today.`; document.getElementById('seoStatGrid').innerHTML = `
Invested
$1,000
Current Value
${fmt(currentValue)}
Profit / Loss
${fmt(profit)}
ROI
${roi >= 0 ? '+' : ''}${roi.toFixed(1)}%
Peak Value
${fmt(peakValue)}
Peak Date
${fmtDate(peakDate)}
`; document.getElementById('seoSummary').style.display = 'block'; document.getElementById('exampleChartTitle').textContent = seoName + ' — $1,000 invested ' + seoLabel; document.getElementById('peakTitle').textContent = seoName + ' — Peak profit moment'; document.getElementById('peakDesc').textContent = `If you had sold at the peak, your $1,000 would have been worth ${peakValue.toLocaleString('en-US', {maximumFractionDigits: 0})}`; document.getElementById('peakAmount').textContent = fmt(peakValue); document.getElementById('peakDate').textContent = fmtDate(peakDate); const faqPeakDate = document.getElementById('faqPeakDate'); const faqPeakValue = document.getElementById('faqPeakValue'); if (faqPeakDate) faqPeakDate.textContent = fmtDate(peakDate); if (faqPeakValue) faqPeakValue.textContent = fmt(peakValue); } catch(e) { console.warn('Could not load SEO chart:', e); } } // ─── CUSTOM DATE PICKER ─────────────────────────────────────────────────────── let _dpMin, _dpMax, _dpSel, _dpView = 'days'; let _dpCurYear, _dpCurMonth; function dpInit(min, max, defaultVal) { // Parse as local midnight to avoid UTC offset shifting the date by a day _dpMin = new Date(min + 'T00:00:00'); _dpMax = new Date(max + 'T00:00:00'); const d = new Date(defaultVal + 'T00:00:00'); _dpCurYear = d.getFullYear(); _dpCurMonth = d.getMonth(); dpSetDate(defaultVal); } function dpToggle() { const popup = document.getElementById('dpPopup'); const input = document.getElementById('dpInput'); const isOpen = popup.classList.contains('open'); if (isOpen) { popup.classList.remove('open'); input.classList.remove('open'); } else { popup.classList.add('open'); input.classList.add('open'); dpRender(); } } function dpClose() { document.getElementById('dpPopup').classList.remove('open'); document.getElementById('dpInput').classList.remove('open'); _dpView = 'days'; } document.addEventListener('click', e => { if (!document.getElementById('dpWrap').contains(e.target)) dpClose(); }); function dpNavMonth(dir) { if (_dpView === 'days') { _dpCurMonth += dir; if (_dpCurMonth > 11) { _dpCurMonth = 0; _dpCurYear++; } if (_dpCurMonth < 0) { _dpCurMonth = 11; _dpCurYear--; } } else if (_dpView === 'years') { _dpCurYear += dir * 12; } dpRender(); } function dpToggleView() { if (_dpView === 'days') _dpView = 'months'; else if (_dpView === 'months') _dpView = 'years'; else _dpView = 'days'; dpRender(); } function dpRender() { const MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December']; const MONTHS_SHORT = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; document.getElementById('dpMonthYear').textContent = MONTHS[_dpCurMonth] + ' ' + _dpCurYear; document.getElementById('dpDayView').style.display = _dpView === 'days' ? '' : 'none'; document.getElementById('dpMonthView').classList.toggle('open', _dpView === 'months'); document.getElementById('dpYearView').classList.toggle('open', _dpView === 'years'); if (_dpView === 'days') { const first = new Date(_dpCurYear, _dpCurMonth, 1).getDay(); const daysInMonth = new Date(_dpCurYear, _dpCurMonth + 1, 0).getDate(); let html = ''; for (let i = 0; i < first; i++) html += '
'; for (let d = 1; d <= daysInMonth; d++) { const ds = _dpCurYear + '-' + String(_dpCurMonth+1).padStart(2,'0') + '-' + String(d).padStart(2,'0'); const dt = new Date(ds + 'T00:00:00'); const today = localDateStr(new Date()); const isSel = ds === _dpSel; const isToday = ds === today; const isDisabled = dt < _dpMin || dt > _dpMax; html += `
${d}
`; } document.getElementById('dpDays').innerHTML = html; } else if (_dpView === 'months') { let html = ''; for (let m = 0; m < 12; m++) { const anyValid = !Array.from({length:new Date(_dpCurYear,m+1,0).getDate()},(_,i)=>{ const dt=new Date(_dpCurYear,m,i+1); return dt>=_dpMin&&dt<=_dpMax; }).every(v=>!v); const isSel = _dpSel && parseInt(_dpSel.slice(5,7))-1===m && parseInt(_dpSel.slice(0,4))===_dpCurYear; html += `
${MONTHS_SHORT[m]}
`; } document.getElementById('dpMonthView').innerHTML = html; } else { const base = Math.floor(_dpCurYear / 12) * 12; let html = ''; for (let y = base; y < base + 12; y++) { const anyValid = !(new Date(y,11,31) < _dpMin || new Date(y,0,1) > _dpMax); const isSel = _dpSel && parseInt(_dpSel.slice(0,4)) === y; html += `
${y}
`; } document.getElementById('dpYearView').innerHTML = html; } } function dpPickDay(ds) { dpSetDate(ds); dpClose(); } function dpPickMonth(m) { _dpCurMonth = m; _dpView = 'days'; dpRender(); } function dpPickYear(y) { _dpCurYear = y; _dpView = 'months'; dpRender(); } function dpSetDate(ds) { _dpSel = ds; document.getElementById('dateInput').value = ds; const d = new Date(ds + 'T00:00:00'); _dpCurYear = d.getFullYear(); _dpCurMonth = d.getMonth(); const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; const display = document.getElementById('dpDisplay'); display.textContent = d.getDate() + ' ' + MONTHS[d.getMonth()] + ' ' + d.getFullYear(); display.classList.remove('dp-placeholder'); } function dpSetQuick(yearsAgo) { const d = new Date(); d.setFullYear(d.getFullYear() - yearsAgo); // Clamp to min/max if (d < _dpMin) d.setTime(_dpMin.getTime()); if (d > _dpMax) d.setTime(_dpMax.getTime()); // Use local date string so NZ/UTC+12/+13 users don't get shifted to the wrong day dpSetDate(localDateStr(d)); dpClose(); }