diff --git a/public/js/calculators.js b/public/js/calculators.js index eabce8b..70d5243 100644 --- a/public/js/calculators.js +++ b/public/js/calculators.js @@ -1,7 +1,6 @@ import { parseAgeMonths, formatParsedAge } from './calculators/age.js'; import { loadEquipmentData, renderEquipmentResult } from './calculators/equipment.js'; import { initImageLightbox } from './calculators/lightbox.js'; -import { calcZ as calcNeonatalZ, classifyBirthWeight, classifyGA, classifyWeight, interpolateLMS, loadFentonLmsData, zToPercentile } from './calculators/neonatal.js'; import { loadResusMedsData, renderResusMeds } from './calculators/resus.js'; import { loadVitalsData, renderVitalsResult } from './calculators/vitals.js'; @@ -1806,567 +1805,3 @@ initImageLightbox(); } } })(); - -// ============================================================ -// NEONATAL ASSESSMENT -// ============================================================ -(function() { - var fentonLMS = { male: {}, female: {} }; - loadFentonLmsData() - .then(function(data) { fentonLMS = data; }) - .catch(function(err) { console.warn('[calculators] Fenton LMS data unavailable:', err && err.message); }); - - document.addEventListener('click', function(e) { - if (e.target.id === 'btn-neo-assess' || e.target.closest('#btn-neo-assess')) neoAssess(); - }); - - function neoAssess() { - var weeks = parseInt(document.getElementById('neo-ga-weeks').value); - var days = parseInt(document.getElementById('neo-ga-days').value) || 0; - var weight = parseFloat(document.getElementById('neo-weight').value); - var sex = document.getElementById('neo-sex').value; - var resultDiv = document.getElementById('neo-result'); - - if (!weeks || !weight) { - resultDiv.innerHTML = '
Enter GA and birth weight.
'; - return; - } - if (!fentonLMS[sex] || Object.keys(fentonLMS[sex]).length === 0) { - resultDiv.innerHTML = 'Fenton growth data is still loading.
'; - return; - } - - var gaDecimal = weeks + days / 7; - var gaClass = classifyGA(weeks, days); - var bwClass = classifyBirthWeight(weight); - - // Calculate percentile using Fenton LMS - var lms = interpolateLMS(fentonLMS[sex], gaDecimal); - var z = calcNeonatalZ(weight, lms.L, lms.M, lms.S); - var percentile = zToPercentile(z); - var wtClass = classifyWeight(percentile); - var expectedWeight = Math.round(lms.M); - - var html = ''; - - // GA Classification - html += 'Fenton TR, Kim JH. A systematic review and meta-analysis to revise the Fenton growth chart for preterm infants. BMC Pediatrics 2013;13:59. GA classification per ACOG/AAP definitions.
'; - - resultDiv.innerHTML = html; - } -})(); - -// ============================================================ -// RESPIRATORY MANAGEMENT (Asthma, Croup, Bronchiolitis) -// ============================================================ -(function() { - // Sub-pill navigation - document.addEventListener('click', function(e) { - var pill = e.target.closest('[data-resp]'); - if (pill) { - document.querySelectorAll('[data-resp]').forEach(function(p) { p.classList.remove('active'); }); - pill.classList.add('active'); - ['asthma','croup','bronchiolitis'].forEach(function(s) { - var el = document.getElementById('resp-' + s); - if (el) el.style.display = pill.dataset.resp === s ? '' : 'none'; - }); - } - if (e.target.closest('[data-asthma-severity]')) asthmaManagement(e.target.closest('[data-asthma-severity]').dataset.asthmaSeverity); - if (e.target.id === 'btn-pram-calc' || e.target.closest('#btn-pram-calc')) calcPRAM(); - if (e.target.id === 'btn-croup-calc' || e.target.closest('#btn-croup-calc')) calcCroup(); - if (e.target.id === 'btn-bronch-calc' || e.target.closest('#btn-bronch-calc')) calcBronchiolitis(); - }); - - function dose(wt, mgPerKg, maxMg) { var d = Math.round(wt * mgPerKg * 10) / 10; return maxMg ? Math.min(d, maxMg) : d; } - // Delegates to the shared _EM.dStr so the per-kg math is visible to the prescriber. - function doseStr(wt, mgPerKg, maxMg, unit) { - return window._EM.dStr(wt, mgPerKg, maxMg, unit); - } - function drugRow(name, doseText, route, notes) { - return '| Drug | Dose | Route | Notes |
|---|
Enter weight (kg).
'; return; } - - var html = ''; - if (severity === 'mild') { - html += severityBadge('Mild Exacerbation', '#10b981'); - html += 'Speaks in sentences, no accessory muscle use, SpO2 ≥94%
'; - html += drugTable( - drugRow('Albuterol (MDI)', '4-8 puffs via spacer', 'Inhaled', 'q20min x 3 doses, then q1-4h') + - drugRow('Albuterol (neb)', doseStr(wt, 0.15, 5, ' mg') + ' (min 2.5 mg)', 'Nebulized', 'q20min x 3 doses') + - drugRow('Dexamethasone', doseStr(wt, 0.6, 16), 'PO/IV', 'Single dose, or 2 days') + - drugRow('Prednisolone', doseStr(wt, 1, 60) + '/day', 'PO', 'Alternative: 3-5 day course') - ); - html += 'Speaks in phrases, some accessory muscle use, SpO2 90-93%
'; - html += drugTable( - drugRow('Albuterol (neb)', doseStr(wt, 0.15, 5, ' mg') + ' (min 2.5 mg)', 'Nebulized', 'q20min x 3 doses, then continuous if needed') + - drugRow('Ipratropium', wt < 20 ? '250 mcg' : '500 mcg', 'Nebulized', 'q20min x 3 doses with albuterol') + - drugRow('Dexamethasone', doseStr(wt, 0.6, 16), 'PO/IV/IM', 'Single dose') + - drugRow('O2 supplemental', 'Target SpO2 ≥94%', 'Nasal cannula/mask', 'Titrate to effect') - ); - html += 'Speaks in words only, significant accessory muscle use, SpO2 <90%. Consider ICU.
'; - html += drugTable( - drugRow('Albuterol continuous', doseStr(wt, 0.5, 20, ' mg') + '/hr', 'Continuous neb', 'Or 0.15-0.3 mg/kg q20min') + - drugRow('Ipratropium', wt < 20 ? '250 mcg' : '500 mcg', 'Nebulized', 'q20min x 3 doses with albuterol') + - drugRow('Dexamethasone', doseStr(wt, 0.6, 16), 'IV', 'Or methylprednisolone 2 mg/kg IV (max 60 mg)') + - drugRow('Magnesium sulfate', doseStr(wt, 50, 2000) + ' IV over 20 min', 'IV', 'Single dose, monitor BP') + - drugRow('Epinephrine (IM)', doseStr(wt, 0.01, 0.5) + ' (1:1000)', 'IM', 'If impending arrest / no IV access') + - drugRow('Terbutaline', doseStr(wt, 0.01, 0.4) + ' SC/IV', 'SC or IV bolus', 'Then 0.1-10 mcg/kg/min infusion') + - drugRow('O2 supplemental', 'Target SpO2 ≥94%', 'High flow / NIPPV', 'Consider BiPAP/CPAP') - ); - html += 'NAEPP/GINA guidelines. Always use clinical judgment. Verify doses against institutional protocols.
'; - resultDiv.innerHTML = html; - } - - function calcPRAM() { - var total = ['pram-spo2','pram-retractions','pram-scalene','pram-air','pram-wheeze'].reduce(function(s, id) { - return s + parseInt(document.getElementById(id).value); - }, 0); - var severity = total <= 3 ? 'Mild' : total <= 7 ? 'Moderate' : 'Severe'; - var color = total <= 3 ? '#10b981' : total <= 7 ? '#f59e0b' : '#ef4444'; - document.getElementById('pram-result').innerHTML = 'Mild (0-3): outpatient management. Moderate (4-7): consider oral steroids + frequent bronchodilators. Severe (8-12): aggressive treatment, consider ICU.
Westley WJ et al. Nebulized racemic epinephrine by IPPB for the treatment of croup. Am J Dis Child 1978. Mild ≤2, Moderate 3-5, Severe 6-11, Impending failure ≥12.
'; - } - - function calcBronchiolitis() { - var wt = parseFloat(document.getElementById('resp-weight').value) || 5; - var age = document.getElementById('bronch-age').value; - var spo2 = document.getElementById('bronch-spo2').value; - var hydration = document.getElementById('bronch-hydration').value; - var distress = document.getElementById('bronch-distress').value; - - var html = '⚠ Age <12 weeks — high risk for apnea. Monitor closely.
'; - html += 'AAP Clinical Practice Guideline: Management of Bronchiolitis in Infants and Children (2014, reaffirmed 2023). RSV is the most common cause (50-80%).
'; - document.getElementById('bronch-result').innerHTML = html; - } -})(); - -// ============================================================ -// SEIZURE MANAGEMENT -// ============================================================ -(function() { - document.addEventListener('click', function(e) { - if (e.target.id === 'btn-seizure-calc' || e.target.closest('#btn-seizure-calc')) calcSeizure(); - }); - - function d(wt, mgPerKg, max) { var v = Math.round(wt * mgPerKg * 100) / 100; return max ? Math.min(v, max) : v; } - function row(phase, drug, dose, route, notes) { - return 'Enter weight (kg).
'; return; } - var S = window._EM; - var d10Low = Math.round(wt * 2 * 10) / 10; - var d10High = Math.round(wt * 5 * 10) / 10; - - var html = 'Based on AES Guidelines 2016 and ESETT (Kapur et al., NEJM 2019). Verify all doses against institutional protocols.
'; - resultDiv.innerHTML = html; - } -})(); - -// ============================================================ -// ANAPHYLAXIS MANAGEMENT -// ============================================================ -(function() { - document.addEventListener('click', function(e) { - if (e.target.id === 'btn-anaph-calc' || e.target.closest('#btn-anaph-calc')) calcAnaphylaxis(); - }); - - function d(wt, mgPerKg, max) { var v = Math.round(wt * mgPerKg * 100) / 100; return max ? Math.min(v, max) : v; } - - // Lookup a drug entry (by name) from window._DRUGS.sections.anaphylaxis, - // falling back to a hardcoded record so rendering stays identical if the - // JSON fetch hasn't resolved (or failed). Returns an object exposing the - // per-kg + max fields needed by S.dStr. - var ANAPH_FALLBACK = { - 'Epinephrine IM': { dose_mg_per_kg: 0.01, max_mg: 0.5, unit: 'mg' }, - 'Normal saline bolus': { dose_mg_per_kg: 20, max_mg: null, unit: 'mL' }, - 'Diphenhydramine': { dose_mg_per_kg: 1.25, max_mg: 50, unit: 'mg' }, - 'Ranitidine': { dose_mg_per_kg: 1, max_mg: 50, unit: 'mg' }, - 'Dexamethasone': { dose_mg_per_kg: 0.6, max_mg: 16, unit: 'mg' }, - 'Methylprednisolone': { dose_mg_per_kg: 2, max_mg: 125, unit: 'mg' }, - 'Glucagon': { dose_mg_per_kg: 0.02, max_mg: 1, unit: 'mg' } - }; - function anaphDrug(name) { - var s = window._DRUGS && window._DRUGS.sections && window._DRUGS.sections.anaphylaxis; - if (s && s.drugs) { - var hit = s.drugs.filter(function(x) { return x.name === name; })[0]; - if (hit) return hit; - } - return ANAPH_FALLBACK[name] || {}; - } - - function calcAnaphylaxis() { - var wt = parseFloat(document.getElementById('anaph-weight').value); - var age = document.getElementById('anaph-age').value; - var resultDiv = document.getElementById('anaph-result'); - if (!wt) { resultDiv.innerHTML = 'Enter weight (kg).
'; return; } - var S = window._EM; - - var epi = anaphDrug('Epinephrine IM'); - var fluids = anaphDrug('Normal saline bolus'); - var dph = anaphDrug('Diphenhydramine'); - var rnt = anaphDrug('Ranitidine'); - var dex = anaphDrug('Dexamethasone'); - var mpn = anaphDrug('Methylprednisolone'); - var glc = anaphDrug('Glucagon'); - - var epiDose = d(wt, epi.dose_mg_per_kg, epi.max_mg); - var epiVol = Math.round(epiDose * 10) / 10; // 1:1000 = 1 mg/mL - var autoInjector = wt < 10 ? 'Draw up manually' : wt <= 25 ? 'EpiPen Jr / Auvi-Q 0.15 mg' : 'EpiPen / Auvi-Q 0.3 mg'; - - var html = '| Step | Drug | Dose | Route | Notes |
|---|---|---|---|---|
| STEP 2 | Position | Supine + legs elevated | — | If dyspneic: sitting position. If vomiting: recovery position |
| STEP 3 | O2 | High flow 10-15 L/min | Face mask | 100% O2. Prepare for airway management |
| STEP 4 | IV fluids | NS ' + S.dStr(wt, fluids.dose_mg_per_kg, fluids.max_mg, ' mL') + ' bolus | IV/IO | Repeat up to 60 mL/kg for hypotension |
| STEP 5 | Diphenhydramine | ' + S.dStr(wt, dph.dose_mg_per_kg, dph.max_mg) + ' | IV/IM/PO | H1 blocker. NOT first-line — adjunct only |
| Ranitidine | ' + S.dStr(wt, rnt.dose_mg_per_kg, rnt.max_mg) + ' | IV over 5 min | H2 blocker. Optional adjunct | |
| STEP 6 | Dexamethasone | ' + S.dStr(wt, dex.dose_mg_per_kg, dex.max_mg) + ' | IV/IM/PO | Prevents biphasic reaction (4-6 hrs later) |
| Methylprednisolone | ' + S.dStr(wt, mpn.dose_mg_per_kg, mpn.max_mg) + ' | IV | Alternative steroid | |
| IF REFRACTORY | Epinephrine gtt | 0.1-1 mcg/kg/min | IV infusion | For persistent hypotension despite fluids + IM epi |
| Glucagon | ' + S.dStr(wt, glc.dose_mg_per_kg, glc.max_mg) + ' | IV/IM | For patients on beta-blockers not responding to epi |
ASCIA Anaphylaxis Guidelines 2021. WAO Anaphylaxis Guidance 2020. AAP/ACAAI Practice Parameters.
'; - resultDiv.innerHTML = html; - } -})(); - -// ============================================================ -// PROCEDURAL SEDATION -// ============================================================ -(function() { - document.addEventListener('click', function(e) { - if (e.target.id === 'btn-sed-calc' || e.target.closest('#btn-sed-calc')) calcSedation(); - }); - - function d(wt, mgPerKg, max) { var v = Math.round(wt * mgPerKg * 100) / 100; return max ? Math.min(v, max) : v; } - - // Fallback inline data — mirrors drugs.json sedation section. The first - // table's rows group by drug (Ketamine has both IV + IM lines), hence the - // display vs name split. `reverses` marks the final 2 rows as reversal - // agents shown in a separate table. - var SED_FALLBACK = [ - { name: 'Ketamine IV', display: 'Ketamine', dose_mg_per_kg_low: 1.5, dose_mg_per_kg_high: 2, max_mg_low: 100, max_mg_high: 150, unit: 'mg', route: 'IV', onset: '1 min', duration: '15-30 min', notes: 'Dissociative. Preserves airway reflexes.' }, - { name: 'Ketamine IM', display: '', dose_mg_per_kg_low: 4, dose_mg_per_kg_high: 5, max_mg_low: 300, max_mg_high: 400, unit: 'mg', route: 'IM', onset: '3-5 min', duration: '30-60 min', notes: 'Give atropine 0.01 mg/kg to reduce secretions.' }, - { name: 'Midazolam IV', display: 'Midazolam', dose_mg_per_kg_low: 0.05, dose_mg_per_kg_high: 0.1, max_mg_low: 2, max_mg_high: 5, unit: 'mg', route: 'IV', onset: '2-3 min', duration: '30-60 min', notes: 'Anxiolysis. Titrate q3-5 min.' }, - { name: 'Midazolam IN/PO', display: '', dose_mg_per_kg: 0.5, max_mg: 20, unit: 'mg', route: 'IN/PO', onset: '10-15 min', duration: '30-60 min', notes: 'IN (max 10 mg / naris) or PO (max 20 mg).' }, - { name: 'Propofol', display: 'Propofol', dose_mg_per_kg: 1, max_mg: 40, unit: 'mg', route: 'IV', onset: '30 sec', duration: '5-10 min', notes: 'Short procedures. Causes apnea — manage airway.' }, - { name: 'Fentanyl IV', display: 'Fentanyl', dose_mg_per_kg: 1, max_mg: 100, unit: 'mcg', route: 'IV', onset: '2-3 min', duration: '30-60 min', notes: 'Analgesic. Often combined with midazolam.' }, - { name: 'Fentanyl IN', display: '', dose_mg_per_kg: 2, max_mg: 100, unit: 'mcg', route: 'IN', onset: '5-10 min', duration: '30-60 min', notes: 'Intranasal.' }, - { name: 'Nitrous oxide', display: 'Nitrous oxide', dose_display: '50:50 or 70:30 mix', unit: 'mix', route: 'Inhaled',onset: '2-5 min', duration: '5 min off', notes: 'Self-administered via demand valve. Anxiolysis + mild analgesia.' }, - { name: 'Dexmedetomidine IN', display: 'Dexmedetomidine', dose_mg_per_kg_low: 2, dose_mg_per_kg_high: 3, max_mg_low: 100, max_mg_high: null, unit: 'mcg', route: 'IN', onset: '15-30 min', duration: '60-90 min', notes: 'Intranasal. No respiratory depression. Good for imaging.' }, - { name: 'Naloxone', display: 'Naloxone', dose_mg_per_kg: 0.1, max_mg: 2, unit: 'mg', route: 'IV/IM/IN', reverses: 'Opioids (fentanyl, morphine)', notes: 'Max 2 mg. Repeat q2-3 min. Duration shorter than opioids — monitor for re-sedation.' }, - { name: 'Flumazenil', display: 'Flumazenil', dose_mg_per_kg: 0.01, max_mg: 0.2, unit: 'mg', route: 'IV', reverses: 'Benzodiazepines (midazolam)', notes: 'Max 0.2 mg single dose. Repeat q1 min to max 1 mg total. Risk of seizures — use cautiously.' } - ]; - - function sedDrugs() { - var s = window._DRUGS && window._DRUGS.sections && window._DRUGS.sections.sedation; - if (s && s.drugs && s.drugs.length) { - return s.drugs.map(function(d) { - var fb = SED_FALLBACK.filter(function(x) { return x.name === d.name; })[0] || {}; - return Object.assign({}, fb, d); - }); - } - return SED_FALLBACK; - } - - // perKg footer that matches the original inline format exactly. - function sedPerKg(lo, hi, unit) { - unit = unit || 'mg'; - return ' (' + lo + (hi != null ? '-' + hi : '') + ' ' + unit + '/kg)'; - } - - // Build the Dose cell text for a sedation drug (supports low/high range - // and single-dose entries; honors custom dose_display for fixed doses). - function sedDoseCell(wt, dg) { - if (dg.dose_display) return dg.dose_display; - var unitLabel = dg.unit === 'mcg' ? ' mcg' : (dg.unit === 'mix' ? '' : ' mg'); - var perKgUnit = dg.unit === 'mcg' ? 'mcg' : 'mg'; - if (dg.dose_mg_per_kg_low != null) { - var lo = d(wt, dg.dose_mg_per_kg_low, dg.max_mg_low); - if (dg.dose_mg_per_kg_high != null) { - var hi = d(wt, dg.dose_mg_per_kg_high, dg.max_mg_high); - return lo + '-' + hi + unitLabel + sedPerKg(dg.dose_mg_per_kg_low, dg.dose_mg_per_kg_high, perKgUnit); - } - return lo + unitLabel + sedPerKg(dg.dose_mg_per_kg_low, null, perKgUnit); - } - if (dg.dose_mg_per_kg != null) { - var v = d(wt, dg.dose_mg_per_kg, dg.max_mg); - return v + unitLabel + sedPerKg(dg.dose_mg_per_kg, null, perKgUnit); - } - return ''; - } - - function sedRow(wt, dg, showReverses) { - var displayName = dg.display == null ? dg.name : dg.display; - var nameCell = displayName - ? 'Enter weight (kg).
'; return; } - - var all = sedDrugs(); - var agents = all.filter(function(dg) { return !dg.reverses; }); - var reversals = all.filter(function(dg) { return !!dg.reverses; }); - - var html = '| Drug | Dose | Route | Onset | Duration | Notes |
|---|
| Drug | Dose | Route | Reverses | Notes |
|---|
AAP Guidelines for Monitoring and Management of Pediatric Patients Before, During, and After Sedation. ASA Practice Guidelines for Sedation and Analgesia by Non-Anesthesiologists.
'; - resultDiv.innerHTML = html; - } -})();