Educational reference only. Nothing here is medical advice. All dosage information comes from research literature, practitioner protocols, and community reporting — not a clinical trial designed for you. You are responsible for your own due diligence. Read full disclaimer →
Hutto's Peptide Resource
Hunter Williams Cheat Sheet × PeptideDosages.com — April 2026
Allevery-fucking-thing
Fat Lossno more fat cunt
Healingun-munt yourself
Longevitylive foreverrrrr
Immunitystop getting sick, ya muppet
Cognitionbecome a smart cunt
Muscleget absolutely JACKED
Hormonalsave the nuts, fire the GH engine
Sexualget the rocket back online
Bioregulatorsoptimise this sacred temple
Otherthe weird shit
🛒 Shopprices & order
📋 Plannerdose + cycle calc
Showing of compounds  ·  click any row for full detail
Compound Purpose Vial BAC Water Sub-Q Dose Units Timing Frequency Duration

💉 Dose Calculator

Select a compound to pre-fill — or enter your own values

⬡ Recommended
✎ My Protocol
Days/week
Every X days
days per week
1 mg = IU  (HGH preset: 3)

Syringe Fill Guide

U-100
1 ml
U-50
0.5 ml
U-30
0.3 ml
Enter vial + dose details above

📋 Cycle & Supply Planner

See how long 10 vials lasts — or how many you need for a cycle

📦 I have 10 vials
How long will they last?
📅 Plan a cycle
How many vials do I need?

📊 Cycle Projection

Enter details to calculate
Doses per vial
Injections / week
Total injections
Total peptide needed
Per Vial Breakdown
Enter dose and frequency to see breakdown.
⚠ Select a compound above, adjust values as needed, then tap Save Protocol before generating your report.
🛒
Order total
0 items
$0
🛒 Shop
📋 Planner

📄 Protocol Report

`; }).join(''); return `

💰 Pricing — per pack of 10 vials (AUD)

${vh}
`; } // ─── CART ───────────────────────────────────────────────────────────────────── function addToCart(code, name, qty, price) { const idx = cart.findIndex(i => i.code === code); if (idx >= 0) { cart[idx].count = (cart[idx].count||1)+1; } else { cart.push({code,name,qty,price,count:1}); } updateCartUI(); refreshPlannerPicker(); document.querySelectorAll('[data-cart-code="' + code + '"]').forEach(b => { b.textContent='✓ Added'; b.classList.add('added'); setTimeout(()=>{b.textContent='+ Cart';b.classList.remove('added');},1200); }); document.querySelectorAll('[data-cart-variant="' + code + '"]').forEach(el => el.classList.add('in-cart')); } function removeFromCart(code) { cart = cart.filter(i => i.code !== code); updateCartUI(); refreshPlannerPicker(); document.querySelectorAll('[data-cart-variant="' + code + '"]').forEach(el => el.classList.remove('in-cart')); } function clearCart() { cart = []; updateCartUI(); document.querySelectorAll('.in-cart').forEach(el => el.classList.remove('in-cart')); closeOverlay('cartOverlay'); } function cartTotal() { return cart.reduce((s,i) => s+i.price*(i.count||1), 0); } function cartCount() { return cart.reduce((s,i) => s+(i.count||1), 0); } function updateCartUI() { const total=cartTotal()+SHIPPING_FLAT, subtotal=cartTotal(), count=cartCount(); const bar=document.getElementById('cartBar'); if (bar) bar.className = count > 0 ? 'show' : ''; const tb=document.getElementById('cartTotalBar'); if(tb) tb.textContent='$'+total; const ci=document.getElementById('cartItemCount'); if(ci) ci.textContent=count+' item'+(count!==1?'s':''); updateFloatBadge(); renderCartModal(); } function addBacWaterToCart() { const sel = document.getElementById('bacWaterVariant'); const qty = parseInt(document.getElementById('bacWaterQty').value) || 1; if (!sel) return; const [code, size, price] = sel.value.split('|'); const priceNum = parseInt(price); // Check if already in cart - update count const existing = cart.findIndex(i => i.code === code); if (existing >= 0) { cart[existing].count = (cart[existing].count || 1) + qty; } else { cart.push({ code, name: 'BAC Water', qty: size, price: priceNum, count: qty }); } updateCartUI(); // Flash feedback const btn = event.target; btn.textContent = '✓ Added'; setTimeout(() => { btn.textContent = '+ Add'; }, 1200); } function renderCartModal() { const list = document.getElementById('cartList'); const summary = document.getElementById('cartSummary'); if (!list) return; if (!cart.length) { list.innerHTML = '
Your order is empty.
Browse the 🛒 Shop tab.
'; if (summary) summary.innerHTML = ''; return; } list.innerHTML = cart.map(item => `
${item.name}
${item.qty}
${item.code}${item.count>1?' × '+item.count:''}
$${item.price*(item.count||1)}
`).join(''); const subtotal = cartTotal(), total = subtotal + SHIPPING_FLAT, count = cartCount(); if (summary) summary.innerHTML = `
${count} item${count!==1?'s':''}$${subtotal}
🚚 Flat rate shipping$${SHIPPING_FLAT}
Total (AUD)$${total}
Per pack of 10 vials. Reference order — confirm with supplier.
💧 BAC WATER (required for reconstitution)
Qty:
`; } // ─── SHOP ───────────────────────────────────────────────────────────────────── function renderShop() { const q = (document.getElementById('shopSearch')?.value||'').toLowerCase(); const cat = document.getElementById('shopCat')?.value||''; const sort= document.getElementById('shopSort')?.value||'name'; const grid= document.getElementById('priceGrid'); if (!grid) return; let entries = Object.entries(PRICES); if (q) entries = entries.filter(([name,variants]) => name.includes(q) || variants.some(v => v.code.toLowerCase().includes(q) || v.qty.toLowerCase().includes(q))); if (cat && SHOP_CATS[cat]) { const kws=SHOP_CATS[cat]; entries=entries.filter(([name])=>kws.some(kw=>name.toLowerCase().includes(kw))); } if (sort==='name') entries.sort((a,b)=>a[0].localeCompare(b[0])); else entries.sort((a,b)=>{ const am=Math.min(...a[1].map(v=>v.price)),bm=Math.min(...b[1].map(v=>v.price)); return sort==='price-asc'?am-bm:bm-am; }); if (!entries.length) { grid.innerHTML='
🛒No products match
'; return; } grid.innerHTML = entries.map(([name,variants]) => { const dn = (variants[0]?.rawName||name).replace(/`/g,'\\`').replace(/\$/g,'\\$'); const desc = variants[0]?.desc||''; const vh = variants.map(v => { const ic = cart.some(c => c.code === v.code); const sq = v.qty.replace(/`/g,'\\`'); return `
${v.code}
${v.qty}
$${v.price}
`; }).join(''); return `
${dn}
${desc}
${vh}
`; }).join(''); } // ─── STANDALONE CALC ───────────────────────────────────────────────────────── function runCalc() { const p=parseFloat(document.getElementById('cP').value),pu=document.getElementById('cPU').value; const w=parseFloat(document.getElementById('cW').value); const d=parseFloat(document.getElementById('cD').value),du=document.getElementById('cDU').value; if(isNaN(p)||isNaN(w)||isNaN(d)||w<=0||d<=0){alert('Fill in all fields.');return;} const res=calcDose(p,pu,w,d,du); const fu=res.volMl*100; document.getElementById('rConc').textContent=fmtConc(res.concVal,res.concUnit); document.getElementById('rVol').textContent=isFinite(res.volMl)?res.volMl.toFixed(3)+' ml':'—'; document.getElementById('rU').textContent=isFinite(fu)?fu.toFixed(1)+' units':'—'; document.getElementById('rD').textContent=res.dosesInVial>0?res.dosesInVial:'—'; document.getElementById('calcResult').classList.add('show'); } function resetCalc(){['cP','cW','cD'].forEach(id=>document.getElementById(id).value='');document.getElementById('calcResult').classList.remove('show');} // ─── PLANNER ───────────────────────────────────────────────────────────────── function setFreqMode(mode) { plannerFreqMode = mode; document.getElementById('fmode-days').classList.toggle('active', mode==='days'); document.getElementById('fmode-every').classList.toggle('active', mode==='every'); document.getElementById('freq-days-row').style.display = mode==='days' ? 'flex' : 'none'; document.getElementById('freq-every-row').style.display = mode==='every' ? 'flex' : 'none'; plannerCalc(); } function setPlanMode(mode) { plannerPlanMode = mode; document.getElementById('plan-mode-supply').classList.toggle('active', mode==='supply'); document.getElementById('plan-mode-duration').classList.toggle('active', mode==='duration'); document.getElementById('supplyModeInputs').style.display = mode==='supply' ? 'block' : 'none'; document.getElementById('durationModeInputs').style.display = mode==='duration' ? 'block' : 'none'; plannerCalc(); } function setPlannerSyr(el, cap) { document.querySelectorAll('.syr-choice-btn').forEach(b => b.classList.remove('active')); el.classList.add('active'); plannerSyrCap = cap; plannerCalc(); } function plannerToggleIU() { const vu=document.getElementById('my-vial-unit')?.value; const du=document.getElementById('my-dose-unit')?.value; const row=document.getElementById('iuToggleRow'); if (row) row.style.display = (vu==='iu'||du==='iu') ? 'flex' : 'none'; } function toggleIUConv() { const row=document.getElementById('iuConvRow'); const btn=document.getElementById('iuConvBtn'); const show=row.classList.contains('show'); row.classList.toggle('show',!show); btn.classList.toggle('active',!show); btn.textContent = show ? '+ Conv. Rate' : '− Conv. Rate'; } function getInjectionsPerWeek() { if (plannerFreqMode==='days') { const d=parseFloat(document.getElementById('my-freq-days')?.value); return isNaN(d) ? null : Math.min(7,Math.max(1,d)); } else { const e=parseFloat(document.getElementById('my-freq-every')?.value); return isNaN(e)||e<=0 ? null : 7/e; } } // ─── SAVE PROTOCOL FROM PLANNER ────────────────────────────────────────────── function saveCurrentProtocol() { const sel = document.getElementById('plannerCompound'); if (!sel || sel.value === '') { alert('Please select a compound first.'); return; } const r = D[parseInt(sel.value)]; saveProtocol(r.name); // Update status const status = document.getElementById('saveProtocolStatus'); if (status) status.textContent = '✓ ' + r.name + ' saved'; } function saveProtocol(compoundName) { if (!compoundName) return; protocolStore[compoundName] = { vialAmt: parseFloat(document.getElementById('my-vial')?.value) || null, vialUnit: document.getElementById('my-vial-unit')?.value || 'mg', bacMl: parseFloat(document.getElementById('my-bac')?.value) || null, doseAmt: parseFloat(document.getElementById('my-dose')?.value) || null, doseUnit: document.getElementById('my-dose-unit')?.value || 'mcg', timing: document.getElementById('my-timing')?.value || '', freqMode: plannerFreqMode, freqDays: parseFloat(document.getElementById('my-freq-days')?.value) || null, freqEvery: parseFloat(document.getElementById('my-freq-every')?.value) || null, duration: '', // filled from compound data savedAt: Date.now(), }; // Visual feedback refreshPlannerPicker(); const btn = document.getElementById('saveProtocolBtn'); if (btn) { btn.textContent = '✓ Saved'; btn.style.background = 'var(--accent)'; btn.style.color = '#000'; setTimeout(() => { btn.textContent = 'Save Protocol'; btn.style.background = ''; btn.style.color = ''; }, 1500); } } function normName(n) { return n.replace(/[\uff00-\uffff]/g,'').toLowerCase().replace(/[^a-z0-9]/g,''); } function stripDose(s) { // Strip trailing dose/qty from price list names BEFORE normalising // e.g. "BPC-157 5mg" → "BPC-157", "Tesamorelin 2mg" → "Tesamorelin" // But NOT "BPC-157 + TB-500 Blend (10 mg)" - only simple trailing doses return s.replace(/\s+\d+(\.\d+)?\s*(mg|mcg|ml|iu|g)(\s+vials?.*)?$/i, '') .replace(/\s+\d+\s*x\s*\d+.*$/i, '') // "10 x 80mg vials" .trim(); } function nameMatch(a, b) { // a = D compound name, b = cart item name (from price list) const aClean = normName(stripDose(a)); const bClean = normName(stripDose(b)); const na = normName(a), nb = normName(b); // 1. Exact match (normalised) if (na === nb) return true; // 2. Match after stripping dose suffix (e.g. "BPC-157 5mg" → "BPC-157") if (aClean === bClean && aClean.length > 2) return true; if (na === bClean && na.length > 2) return true; // 3. Alias map: ONLY use cart name (b/nb/bClean) as key → D name as value // Never use D name (a) as key — prevents self-referential matches const mapped = CART_TO_COMPOUND[nb] || CART_TO_COMPOUND[bClean]; if (mapped && na === mapped) return true; // 4. Strict prefix (ratio >= 0.9, min 6 chars — very tight to prevent false positives) const shorter = na.length <= nb.length ? na : nb; const longer = na.length <= nb.length ? nb : na; if (longer.startsWith(shorter) && shorter.length >= 6) { return shorter.length / longer.length >= 0.9; } return false; } // Map from price list rawName (normalised) → D array compound name (normalised) const CART_TO_COMPOUND = { // Name normalisations 'acu': 'acu', 'admax': 'adamax', 'bacwater': 'bacwater', 'botulinumtoxin': 'botulinumtoxin', 'cerbroysin': 'cerebrolysin', 'crystagen': 'crystagen', 'dermorphin': 'dermorphin', 'epithalon': 'epitalon', 'epo': 'epo', 'gluthathione': 'glutathione', 'hexarelin': 'hexarelin', 'hghfragment176191': 'aod9604', 'igflr31': 'igf1lr3', 'lc526': 'lc526', 'lemonbottle': 'lemonbottle', 'lipoc': 'lipoc', 'lipocwithb12': 'lipocwithb12', 'mt1': 'melanotan1', 'melanotan2': 'melanotanii', 'melanotan21210620806': 'melanotanii', 'melanotan2uff081210620806uff09': 'melanotanii', 'thymalin': 'thyamlinthymogen', 'vasoactiveintestinalpeptidevip': 'vipvasoactiveintestinalpeptide', 'oxytocinacetate': 'oxytocin', 'sermorelinacetate': 'sermorelin', 'hgh191': 'hgh191aa', 'hgh191aa': 'hgh191aa', 'aod': 'aod9604', 'mgf': 'mgfmechanogrowthfactor', 'selank': 'selanknacetylselankamidate', 'cagrilintide5mgsemaglutide5mg': 'cagrisemacagrilintidesemaglutide', 'kisspeptin10': 'kisspeptin', 'semaglutidesm': 'semaglutide', 'semaglutideuff08smuff09': 'semaglutide', 'semaglutidesm': 'semaglutide', 'semax': 'semaxnacetylsemaxamidate', 'bpc157tb500': 'bpc157tb500blend10mg', 'cjc1295withoutdac': 'cjc1295nodac', 'cjc1295withdac': 'cjc1295withdac', 'cjc1295withoutdacipa': 'ipamorelincjc1295nodac', 'cjc1295nodac': 'cjc1295nodac', 'glow': 'glowblendghkcubpc157tb500', 'klow': 'klowblendghkcubpc157tb500kpv', 'cagrilintide5mgsemaglutide5mg': 'cagrisemacagrilintidesthemaglutide', 'cagrilintide5mgsemaglutide5mg': 'cagrisemacagrilintidesthemaglutide', 'selank': 'selanknacetylselankamidate', 'cagrilintide5mgsemaglutide5mg': 'cagrisemacagrilintidesemaglutide', 'kisspeptin10': 'kisspeptin', 'semaglutidesm': 'semaglutide', 'semaglutideuff08smuff09': 'semaglutide', 'semaglutidesm': 'semaglutide', 'admax': 'adamax', 'cerbroysin': 'cerebrolysin', 'mgfmechanogrowthfactor': 'mgfmechanogrowthfactor', }; function findProtocol(cartItemName) { // Direct match first if (protocolStore[cartItemName]) return protocolStore[cartItemName]; const cn = normName(cartItemName); // Check alias map → look for D compound with that normalised name const mapped = CART_TO_COMPOUND[cn]; if (mapped) { for (const key of Object.keys(protocolStore)) { if (normName(key) === mapped) return protocolStore[key]; } } // Strict match - cart name vs saved protocol keys for (const key of Object.keys(protocolStore)) { if (nameMatch(key, cartItemName)) return protocolStore[key]; } return null; } function isProtocolComplete(name) { const p = findProtocol(name); if (!p) return false; return p.vialAmt > 0 && p.bacMl > 0 && p.doseAmt > 0; } function getProtocolSummary(name) { const p = findProtocol(name); if (!p) return null; // Format frequency string let freqStr = ''; if (p.freqMode === 'days' && p.freqDays) freqStr = p.freqDays + ' days/week'; else if (p.freqMode === 'every' && p.freqEvery) freqStr = 'every ' + p.freqEvery + ' days'; // Calculate syringe fill let syringeInfo = '—'; if (p.vialAmt && p.bacMl && p.doseAmt) { const vb = p.vialUnit === 'mg' ? p.vialAmt * 1000 : p.vialAmt; const db = p.doseUnit === 'mg' ? p.doseAmt * 1000 : p.doseAmt; const conc = vb / p.bacMl; const vol = db / conc; const units = vol * 100; if (isFinite(vol) && vol > 0) syringeInfo = vol.toFixed(3) + ' ml = ' + units.toFixed(1) + ' units (U-100)'; } return { ...p, freqStr, syringeInfo }; } function initPlannerPicker() { refreshPlannerPicker(); } function refreshPlannerPicker() { const sel = document.getElementById('plannerCompound'); if (!sel) return; // Save current selection const currentVal = sel.value; // Clear existing options except the first while (sel.options.length > 1) sel.remove(1); if (!cart.length) { const o = document.createElement('option'); o.value = ''; o.textContent = '— Add compounds to cart first —'; o.disabled = true; sel.appendChild(o); sel.value = ''; return; } // Match cart items to D array compounds using fuzzy name matching // Cart names come from price list (e.g. "BPC-157 5mg") // D names are canonical (e.g. "BPC-157") // Use global nameMatch function const cartCompounds = D.filter(r => cart.some(i => nameMatch(r.name, i.name))); if (!cartCompounds.length) { const o = document.createElement('option'); o.value = ''; o.textContent = '— No plannable compounds in cart —'; o.disabled = true; sel.appendChild(o); sel.value = ''; return; } cartCompounds.sort((a,b) => a.name.localeCompare(b.name)); cartCompounds.forEach(r => { const o = document.createElement('option'); o.value = D.indexOf(r); // Show save status const saved = isProtocolComplete(r.name); o.textContent = (saved ? '✓ ' : '◦ ') + r.name; sel.appendChild(o); }); // Restore selection if still valid if (currentVal && [...sel.options].some(o => o.value === currentVal)) { sel.value = currentVal; } } function plannerLoadCompound() { const idx=document.getElementById('plannerCompound').value; if (idx==='') { ['rec-vial','rec-bac','rec-dose','rec-timing','rec-freq','rec-dur'].forEach(id=>{ const el=document.getElementById(id); if(el) el.textContent='—'; }); plannerCalc(); return; } const r=D[parseInt(idx)]; const set=(id,val)=>{ const el=document.getElementById(id); if(el) el.textContent=val; }; set('rec-vial',r.vial); set('rec-bac',r.bac); set('rec-dose',r.dose); set('rec-timing',r.timing); set('rec-freq',r.frequency); set('rec-dur',r.duration); const di=parseDoseStr(r.dose), bml=parseBacStr(r.bac), vi=parseDoseStr(r.vial); const inp=(id,val)=>{ const el=document.getElementById(id); if(el) el.value=val; }; const sel=(id,val)=>{ const el=document.getElementById(id); if(el) el.value=val; }; if (vi) { inp('my-vial',vi.val); sel('my-vial-unit',vi.unit); } if (bml) inp('my-bac',bml); if (di) { inp('my-dose',di.val); sel('my-dose-unit',di.unit); } inp('my-timing',r.timing); const freq=r.frequency.toLowerCase(); if (freq.includes('every day')||freq.includes('daily')) { setFreqMode('days'); inp('my-freq-days',7); } else if (freq.match(/5\s*days/)) { setFreqMode('days'); inp('my-freq-days',5); } else if (freq.match(/3[x\s]*(?:per\s*)?week/)) { setFreqMode('days'); inp('my-freq-days',3); } else if (freq.match(/2[x\s]*(?:per\s*)?week/)) { setFreqMode('days'); inp('my-freq-days',2); } else if (freq.includes('once')||freq.match(/1[x\s]*(?:per\s*)?week/)) { setFreqMode('days'); inp('my-freq-days',1); } else if (freq.includes('every other')) { setFreqMode('every'); inp('my-freq-every',2); } else { setFreqMode('days'); inp('my-freq-days',5); } plannerToggleIU(); plannerCalc(); } function plannerCalc() { const vAmt=parseFloat(document.getElementById('my-vial')?.value); const vUnit=document.getElementById('my-vial-unit')?.value||'mg'; const bac=parseFloat(document.getElementById('my-bac')?.value); const dAmt=parseFloat(document.getElementById('my-dose')?.value); const dUnit=document.getElementById('my-dose-unit')?.value||'mcg'; const conv=parseFloat(document.getElementById('iuConvRate')?.value)||0; const inj=getInjectionsPerWeek(); const svgEl=document.getElementById('plannerSyrSVG'); const maxMl=maxMlForCap(plannerSyrCap); const setText=(id,v)=>{ const el=document.getElementById(id); if(el) el.textContent=v; }; const setPull=(pull,sub,ml)=>{ const pe=document.getElementById('fillPull'),se=document.getElementById('fillSub'),me=document.getElementById('fillMlLabel'); if(pe){pe.textContent=pull;pe.style.color='var(--accent2)';} if(se) se.textContent=sub; if(me) me.textContent=ml||''; }; if (!isNaN(vAmt)&&!isNaN(bac)&&!isNaN(dAmt)&&bac>0&&dAmt>0&&vAmt>0) { let vBase,dBase; if (vUnit==='iu'&&dUnit==='iu') { vBase=vAmt; dBase=dAmt; } else if (vUnit==='iu'&&conv>0) { vBase=vAmt*(1000/conv); dBase=toBase(dAmt,dUnit); } else if (dUnit==='iu'&&conv>0) { vBase=toBase(vAmt,vUnit); dBase=dAmt*(1000/conv); } else { vBase=toBase(vAmt,vUnit); dBase=toBase(dAmt,dUnit); } const conc=vBase/bac, vol=dBase/conc, fu=vol*plannerSyrCap; const dosesPerVial=vBase/dBase; if (svgEl) drawSyringe(svgEl,isFinite(vol)?vol:0,maxMl,plannerSyrCap); if (isFinite(fu)&&fu>0) { if (vol>maxMl) { setPull('⚠ OVER','Exceeds syringe — split or use larger',''); if(document.getElementById('fillPull')) document.getElementById('fillPull').style.color='#ff6b6b'; } else { setPull('Pull to '+fu.toFixed(1)+' units','on your '+(plannerSyrCap===100?'U-100':plannerSyrCap===50?'U-50':'U-30')+' syringe',vol.toFixed(3)+' ml per injection'); } } else { setPull('—','Enter vial + dose above',''); } setText('planDosesPerVial',isFinite(dosesPerVial)?Math.floor(dosesPerVial):'—'); if (inj&&isFinite(dosesPerVial)) { setText('planInjectionsPerWeek',inj%1===0?inj.toString():inj.toFixed(1)); if (plannerPlanMode==='supply') { const vials=parseFloat(document.getElementById('plan-vials')?.value)||10; const totalD=dosesPerVial*vials, totalDays=totalD/(inj/7); const wks=totalDays/7, mths=totalDays/30.44; let durStr,durLabel,durSub; if(wks<4){durStr=wks.toFixed(1);durLabel='weeks';durSub='('+Math.round(totalDays)+' days)';} else if(mths<12){durStr=mths.toFixed(1);durLabel='months';durSub='('+Math.round(wks)+' weeks)';} else{durStr=(mths/12).toFixed(1);durLabel='years';durSub='('+Math.round(mths)+' months)';} setText('planHighlightVal',durStr); setText('planHighlightLabel',vials+' vial'+(vials!==1?'s':'')+' will last '+durLabel); setText('planHighlightSub',durSub); setText('planTotalInjections',Math.floor(totalD).toString()); const tp=dBase*Math.floor(totalD); const tpStr=vUnit==='iu'||dUnit==='iu'?tp.toFixed(0)+' IU':tp>=1000?(tp/1000).toFixed(2)+' mg':tp.toFixed(0)+' mcg'; setText('planTotalPeptide',tpStr); const daysPerVial=dosesPerVial/(inj/7), wksPerVial=daysPerVial/7; const bd=document.getElementById('planBreakdown'); if(bd) bd.innerHTML=''+Math.floor(dosesPerVial)+' doses per vial
'+ ''+wksPerVial.toFixed(1)+' weeks per vial ('+Math.round(daysPerVial)+' days)
'+ ''+Math.floor(totalD)+' total injections from '+vials+' vials
'+ 'At '+inj.toFixed(1)+' injections/week'; } else { const durVal=parseFloat(document.getElementById('plan-weeks')?.value)||0; const durUnit=document.getElementById('plan-weeks-unit')?.value||'weeks'; let dd=durUnit==='weeks'?durVal*7:durUnit==='months'?durVal*30.44:durVal; if(dd>0){ const ti=(inj/7)*dd, vn=Math.ceil(ti/dosesPerVial); const tp=dBase*Math.ceil(ti); const tpStr=vUnit==='iu'||dUnit==='iu'?tp.toFixed(0)+' IU':tp>=1000?(tp/1000).toFixed(2)+' mg':tp.toFixed(0)+' mcg'; setText('planHighlightVal',vn.toString()); setText('planHighlightLabel','vials needed for your cycle'); setText('planHighlightSub',durVal+' '+durUnit+' · '+Math.ceil(ti)+' injections'); setText('planTotalInjections',Math.ceil(ti).toString()); setText('planTotalPeptide',tpStr); const bd=document.getElementById('planBreakdown'); if(bd) bd.innerHTML='Cycle: '+durVal+' '+durUnit+' ('+Math.round(dd)+' days)
'+ 'Total injections: '+Math.ceil(ti)+'
'+ 'Vials required: '+vn+'
'+ 'Total peptide: '+tpStr+''; } } } } else { if(svgEl) drawSyringe(svgEl,0,maxMl,plannerSyrCap); setPull('—','Select a compound or enter values above',''); ['planDosesPerVial','planInjectionsPerWeek','planTotalInjections','planTotalPeptide'].forEach(id=>setText(id,'—')); setText('planHighlightVal','—'); setText('planHighlightLabel','Enter dose and frequency'); setText('planHighlightSub',''); const bd=document.getElementById('planBreakdown'); if(bd) bd.textContent='Enter dose and frequency to see breakdown.'; } } // ─── INIT ───────────────────────────────────────────────────────────────────── function init() { // Populate purpose dropdown const purposes = [...new Set(D.map(r => r.purpose))].sort(); const sel = document.getElementById('fPurpose'); if (sel) purposes.forEach(p => { const o=document.createElement('option'); o.value=p; o.textContent=p; sel.appendChild(o); }); // Stats const st=document.getElementById('statTotal'); if(st) st.textContent=D.length; const sn=document.getElementById('statNew'); if(sn) sn.textContent=D.filter(r=>r.src==='peptidedosages').length; const sns=document.getElementById('statNasal'); if(sns) sns.textContent=D.filter(r=>r.nasal).length; // Events — main table const searchEl=document.getElementById('search'); if(searchEl) searchEl.addEventListener('input', renderTable); const fpEl=document.getElementById('fPurpose'); if(fpEl) fpEl.addEventListener('change', renderTable); const ftEl=document.getElementById('fTiming'); if(ftEl) ftEl.addEventListener('change', renderTable); // Events — shop const ss=document.getElementById('shopSearch'); if(ss) ss.addEventListener('input',renderShop); const sc=document.getElementById('shopCat'); if(sc) sc.addEventListener('change',renderShop); const so=document.getElementById('shopSort'); if(so) so.addEventListener('change',renderShop); // Events — planner ['plan-vials','plan-weeks','my-vial','my-bac','my-dose','my-timing','my-freq-days','my-freq-every','iuConvRate'].forEach(id => { const el=document.getElementById(id); if(el) el.addEventListener('input',plannerCalc); }); ['my-vial-unit','my-dose-unit','plan-weeks-unit'].forEach(id => { const el=document.getElementById(id); if(el) el.addEventListener('change',()=>{plannerToggleIU();plannerCalc();}); }); initPlannerPicker(); try { initMainSyringe(); } catch(e) { console.warn('initMainSyringe:', e.message); } renderTable(); } // ── FLOATING PILLS + PDF ── // ─── FLOATING PILLS ────────────────────────────────────────────────────────── function setTabByName(name) { const el = document.querySelector('.tab[data-tab="' + name + '"]'); if (el) setTab(el); // Scroll tab into view if (el) el.scrollIntoView({behavior:'smooth', block:'nearest', inline:'center'}); } // Update cart badge on floating pill function updateFloatBadge() { const count = cartCount(); const badge = document.getElementById('floatCartCount'); if (!badge) return; if (count > 0) { badge.textContent = count; badge.style.display = 'inline-block'; } else { badge.style.display = 'none'; } } const SHIPPING_FLAT = 50; // ─── PDF REPORT ─────────────────────────────────────────────────────────────── function openPDFReport() { if (!cart.length) { alert('Add compounds to your cart first.'); return; } // Check which cart items have saved protocols // Only require protocols for compounds that exist in the D array (have dosing data) // Items like BAC water, Lemon Bottle etc are price-only and exempt const NO_PROTOCOL_NEEDED = new Set(['BAC Water','bacwater','ACU', 'Dermorphin','dermorphin','EPO','epo','lc526','LC526', 'Lemon Bottle','lemon bottle','Lipo C','lipo c', 'Lipo C with B12','lipo c with b12','Botulinum Toxin','botulinum toxin']); const missing = cart.filter(item => { if (NO_PROTOCOL_NEEDED.has(item.name)) return false; if (['BA3','BA10'].includes(item.code)) return false; // Check if item has a D array compound (if not, exempt) const hasCompound = D.some(d => nameMatch(d.name, item.name)); if (!hasCompound) return false; return !isProtocolComplete(item.name); }).map(item => item.name); if (missing.length > 0) { const msg = 'Please complete and save your protocol for:\n\n• ' + missing.join('\n• ') + '\n\nGo to the 📋 Planner tab, select each compound, adjust values and tap Save Protocol.'; alert(msg); // Switch to planner setTabByName('planner'); return; } renderPDFPreview(); openOverlay('pdfOverlay'); } function renderPDFPreview() { const preview = document.getElementById('pdfPreview'); if (!preview) return; const subtotal = cartTotal(); const total = subtotal + SHIPPING_FLAT; let rows = cart.map(item => { const p = protocolStore[item.name]; const complete = isProtocolComplete(item.name); const doseStr = p ? p.doseAmt + ' ' + p.doseUnit : '—'; const freqStr = p ? p.freqStr : '—'; const statusIcon = complete ? '✓' : '⚠'; const statusColor = complete ? 'var(--accent)' : '#ff9900'; return `
${statusIcon} ${item.name}
${complete ? doseStr + ' · ' + freqStr : 'Protocol not saved — go to Planner tab'}
$${item.price * (item.count || 1)}
`; }).join(''); rows += `
🚚 Flat rate shipping$${SHIPPING_FLAT}
`; rows += `
Total (AUD)$${total}
`; preview.innerHTML = rows; } function generatePDF() { const { jsPDF } = window.jspdf; if (!jsPDF) { alert('PDF library not loaded. Check your internet connection.'); return; } const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' }); const pageW = doc.internal.pageSize.getWidth(); const pageH = doc.internal.pageSize.getHeight(); const margin = 18; const gold = [245, 200, 0]; const dark = [10, 10, 20]; const mid = [30, 30, 50]; const light= [240, 240, 248]; const purple = [124, 106, 255]; // ── HEADER ── doc.setFillColor(...dark); doc.rect(0, 0, pageW, 38, 'F'); // Gold accent bar doc.setFillColor(...gold); doc.rect(0, 0, 4, 38, 'F'); doc.setTextColor(...gold); doc.setFont('helvetica', 'bold'); doc.setFontSize(22); doc.text("HUTTO'S PEPTIDE RESOURCE", margin + 2, 16); doc.setFont('helvetica', 'normal'); doc.setFontSize(9); doc.setTextColor(180, 170, 120); doc.text('Protocol Report · For research & educational use only', margin + 2, 24); const dateStr = new Date().toLocaleDateString('en-AU', { day:'numeric', month:'long', year:'numeric' }); doc.text('Generated: ' + dateStr, margin + 2, 31); // ── DISCLAIMER ── doc.setFillColor(25, 20, 5); doc.rect(0, 38, pageW, 12, 'F'); doc.setTextColor(180, 160, 80); doc.setFontSize(7.5); doc.setFont('helvetica', 'italic'); doc.text('⚠ This report is for educational reference only. Not medical advice. Consult a qualified practitioner before starting any peptide protocol.', margin, 45, { maxWidth: pageW - margin * 2 }); let y = 58; // ── COMPOUND TABLE ── doc.setFont('helvetica', 'bold'); doc.setFontSize(12); doc.setTextColor(...dark); doc.text('Selected Compounds', margin, y); y += 6; // Build table data const tableHead = [['Compound', 'Vial', 'BAC Water', 'Dose', 'Timing', 'Frequency', 'Duration', 'Price']]; const tableBody = cart.map(item => { const c = D.find(d => d.name.toLowerCase() === item.name.toLowerCase()) || D.find(d => findVariants && findVariants(d.name) && findVariants(d.name).some(v => v.code === item.code)); const p = protocolStore[item.name]; // user's saved protocol // Use saved protocol values where available, fall back to compound defaults const vialDisplay = p ? p.vialAmt + ' ' + p.vialUnit : (c ? c.vial : item.qty); const bacDisplay = p ? p.bacMl + ' ml' : (c ? c.bac : '—'); const doseDisplay = p ? p.doseAmt + ' ' + p.doseUnit : (c ? c.dose : '—'); const timingDisplay = p ? (p.timing || (c ? c.timing : '—')) : (c ? c.timing : '—'); const freqDisplay = p ? (p.freqStr || (c ? c.frequency : '—')) : (c ? c.frequency : '—'); const durDisplay = c ? c.duration : '—'; return [ item.name, vialDisplay, bacDisplay, doseDisplay, timingDisplay, freqDisplay, durDisplay, '$' + (item.price * (item.count || 1)) ]; }); doc.autoTable({ startY: y, head: tableHead, body: tableBody, margin: { left: margin, right: margin }, headStyles: { fillColor: dark, textColor: gold, fontStyle: 'bold', fontSize: 8, cellPadding: 4, }, bodyStyles: { fontSize: 7.5, cellPadding: 3.5, textColor: [30, 30, 50], }, alternateRowStyles: { fillColor: [248, 247, 255] }, columnStyles: { 0: { fontStyle: 'bold', cellWidth: 32 }, 1: { cellWidth: 18 }, 2: { cellWidth: 18 }, 3: { cellWidth: 20 }, 4: { cellWidth: 18 }, 5: { cellWidth: 32 }, 6: { cellWidth: 28 }, 7: { cellWidth: 16, halign: 'right', fontStyle: 'bold' }, }, didDrawPage: (data) => { // Footer on each page doc.setFontSize(7); doc.setTextColor(160, 155, 130); doc.setFont('helvetica', 'normal'); doc.text("Hutto's Peptide Resource · Research use only · Not medical advice", margin, pageH - 8); doc.text('Page ' + data.pageNumber, pageW - margin, pageH - 8, { align: 'right' }); } }); y = doc.lastAutoTable.finalY + 10; // ── PROTOCOL NOTES TABLE ── if (y < pageH - 60) { doc.setFont('helvetica', 'bold'); doc.setFontSize(12); doc.setTextColor(...dark); doc.text('Protocol Notes', margin, y); y += 6; const notesHead = [['Compound', 'Syringe Fill (U-100)', 'Nasal Route', 'Notes']]; const notesBody = cart.map(item => { const c = D.find(d => d.name.toLowerCase() === item.name.toLowerCase()) || D.find(d => findVariants && findVariants(d.name) && findVariants(d.name).some(v => v.code === item.code)); const p = protocolStore[item.name]; // Use user's saved protocol for syringe calc if available let syringeInfo = p ? p.syringeInfo : '—'; if (!syringeInfo || syringeInfo === '—') { if (c) { const di = parseDoseStr(c.dose), bml = parseBacStr(c.bac), vi = parseDoseStr(c.vial); if (di && bml && vi) { const pb = di.unit === 'mg' ? di.val * 1000 : di.val; const vb = vi.unit === 'mg' ? vi.val * 1000 : vi.val; const conc = vb / bml; const vol = pb / conc; const units= vol * 100; if (isFinite(units)) syringeInfo = vol.toFixed(3) + ' ml = ' + units.toFixed(1) + ' units'; } } } return [ item.name, syringeInfo, c && c.nasal ? (c.nasalDose || '✓ available') : 'Sub-Q only', c && c.note ? c.note.substring(0, 80) + (c.note.length > 80 ? '…' : '') : '—', ]; }); doc.autoTable({ startY: y, head: notesHead, body: notesBody, margin: { left: margin, right: margin }, headStyles: { fillColor: [12, 10, 30], textColor: [180, 160, 255], fontStyle: 'bold', fontSize: 8, cellPadding: 4, }, bodyStyles: { fontSize: 7.5, cellPadding: 3.5, textColor: [30, 30, 50] }, alternateRowStyles: { fillColor: [248, 248, 255] }, columnStyles: { 0: { fontStyle: 'bold', cellWidth: 32 }, 1: { cellWidth: 36 }, 2: { cellWidth: 24 }, 3: { cellWidth: 'auto' }, }, }); y = doc.lastAutoTable.finalY + 10; } // ── ORDER SUMMARY ── if (y > pageH - 55) { doc.addPage(); y = 20; } const subtotal = cartTotal(); const total = subtotal + SHIPPING_FLAT; const boxW = 90; const boxX = pageW - margin - boxW; doc.setFillColor(...dark); doc.roundedRect(boxX, y, boxW, 44, 3, 3, 'F'); doc.setFillColor(...gold); doc.roundedRect(boxX, y, 3, 44, 1.5, 1.5, 'F'); doc.setFont('helvetica', 'bold'); doc.setFontSize(10); doc.setTextColor(...gold); doc.text('ORDER SUMMARY', boxX + 8, y + 10); doc.setFont('helvetica', 'normal'); doc.setFontSize(9); doc.setTextColor(200, 195, 160); doc.text('Subtotal (' + cartCount() + ' items)', boxX + 8, y + 20); doc.text('$' + subtotal, boxX + boxW - 6, y + 20, { align: 'right' }); doc.text('Flat rate shipping', boxX + 8, y + 28); doc.text('$' + SHIPPING_FLAT, boxX + boxW - 6, y + 28, { align: 'right' }); doc.setDrawColor(...gold); doc.setLineWidth(0.4); doc.line(boxX + 6, y + 32, boxX + boxW - 6, y + 32); doc.setFont('helvetica', 'bold'); doc.setFontSize(11); doc.setTextColor(...gold); doc.text('TOTAL (AUD)', boxX + 8, y + 40); doc.text('$' + total, boxX + boxW - 6, y + 40, { align: 'right' }); // ── DISCLAIMER FOOTER ── y += 54; if (y > pageH - 30) { doc.addPage(); y = 20; } doc.setFillColor(20, 15, 5); doc.rect(margin, y, pageW - margin * 2, 20, 'F'); doc.setFont('helvetica', 'italic'); doc.setFontSize(7); doc.setTextColor(160, 148, 100); doc.text( 'DISCLAIMER: All information in this report is for educational and research purposes only. It does not constitute medical advice, diagnosis, or treatment. ' + 'Dosing protocols are reference starting points only. Individual responses vary. Always consult a qualified healthcare practitioner before starting any protocol. ' + "Hutto's Peptide Resource accepts no liability for outcomes resulting from use of this information.", margin + 4, y + 7, { maxWidth: pageW - margin * 2 - 8 } ); doc.save("huttos-peptide-protocol-" + new Date().toISOString().slice(0,10) + ".pdf"); closeOverlay('pdfOverlay'); } window.onload = function() { // Show build timestamp to confirm correct file is loaded var buildEl = document.getElementById('buildTs'); if (buildEl) buildEl.textContent = 'Build: 1781550009'; try { init(); } catch(e) { document.body.insertAdjacentHTML('afterbegin', '
' + 'JS ERROR: ' + e.message + '
' + e.stack + '
' ); console.error('init() error:', e); } }; + e.stack + '
' ); console.error('init() error:', e); } }; save("huttos-peptide-protocol-" + new Date().toISOString().slice(0,10) + ".pdf"); closeOverlay('pdfOverlay'); } window.onload = function() { try { init(); } catch(e) { document.body.insertAdjacentHTML('afterbegin', '
' + 'JS ERROR: ' + e.message + '
' + e.stack + '
' ); console.error('init() error:', e); } }; slice(0,10) + ".pdf"); closeOverlay('pdfOverlay'); } window.onload = function() { init(); };