« MediaWiki:Common.js » : différence entre les versions
Page de l’interface de MediaWiki
Autres actions
Aucun résumé des modifications |
Aucun résumé des modifications |
||
| (6 versions intermédiaires par le même utilisateur non affichées) | |||
| Ligne 1 : | Ligne 1 : | ||
// Forcer l'ouverture des sections hidden dans citizen qui les cachent par défaut | |||
function ouvrirSections() { | function ouvrirSections() { | ||
document.querySelectorAll('.citizen-section').forEach(function (el) { | document.querySelectorAll('.citizen-section').forEach(function (el) { | ||
| Ligne 14 : | Ligne 16 : | ||
new MutationObserver(ouvrirSections).observe(document.documentElement, { childList: true, subtree: true }); | new MutationObserver(ouvrirSections).observe(document.documentElement, { childList: true, subtree: true }); | ||
// ============================================ | // ============================================ | ||
// CALCULATEUR IRC — Microguide | // CALCULATEUR IRC — Microguide | ||
| Ligne 40 : | Ligne 25 : | ||
#calc-app * { box-sizing: border-box; font-family: 'Segoe UI', system-ui, sans-serif; } | #calc-app * { box-sizing: border-box; font-family: 'Segoe UI', system-ui, sans-serif; } | ||
#calc-app { background: #f0f4f8; padding: 16px; border-radius: 12px; } | #calc-app { background: #f0f4f8; padding: 16px; border-radius: 12px; } | ||
.calc-header { background: linear-gradient(135deg, # | .calc-header { background: linear-gradient(135deg, #1a5fa8 0%, #2980d4 100%); padding: 18px 22px; border-radius: 12px; color: white; margin-bottom: 16px; } | ||
.calc-header h1 { margin: 0; font-size: 20px; font-weight: 700; } | .calc-header h1 { margin: 0; font-size: 20px; font-weight: 700; } | ||
.calc-header p { margin: 4px 0 0; font-size: 12px; opacity: 0.75; } | .calc-header p { margin: 4px 0 0; font-size: 12px; opacity: 0.75; } | ||
| Ligne 384 : | Ligne 369 : | ||
var state = { | var state = { | ||
age: '', poids: '', taille: '', creat: '', sexe: 'H', | age: '', poids: '', taille: '', creat: '', sexe: 'H', | ||
selectedCat: '', selectedDrug: '', search: '' | selectedCat: '', selectedDrug: '', search: '', | ||
mode: 'cg', dfgDirect: '' | |||
}; | }; | ||
| Ligne 392 : | Ligne 378 : | ||
function getActiveDose(doses, clcr) { | function getActiveDose(doses, clcr) { | ||
var clcrRounded = Math.round(clcr * 10) / 10; | |||
for (var i = 0; i < doses.length; i++) { | for (var i = 0; i < doses.length; i++) { | ||
if ( | if (clcrRounded >= doses[i].min && clcrRounded <= doses[i].max) return i; | ||
} | |||
for (var i = doses.length - 1; i >= 0; i--) { | |||
if (clcrRounded >= doses[i].min) return i; | |||
} | } | ||
return doses.length - 1; | return doses.length - 1; | ||
} | } | ||
function | function calcClcrFromState() { | ||
var | if (state.mode === 'direct') { | ||
var dfg = parseFloat(state.dfgDirect); | |||
return { clcr: isNaN(dfg) ? null : dfg, imc: null, pi: null, poidsCalc: null }; | |||
} | |||
var age = parseFloat(state.age); | var age = parseFloat(state.age); | ||
var poids = parseFloat(state.poids); | var poids = parseFloat(state.poids); | ||
var taille = parseFloat(state.taille); | var taille = parseFloat(state.taille); | ||
var creat = parseFloat(state.creat); | var creat = parseFloat(state.creat); | ||
var imc = (!isNaN(poids) && !isNaN(taille)) ? calcIMC(poids, taille) : null; | var imc = (!isNaN(poids) && !isNaN(taille)) ? calcIMC(poids, taille) : null; | ||
var pi = (!isNaN(taille)) ? calcPI(taille, state.sexe) : null; | var pi = (!isNaN(taille)) ? calcPI(taille, state.sexe) : null; | ||
| Ligne 414 : | Ligne 404 : | ||
} | } | ||
var clcr = (!isNaN(age) && poidsCalc !== null && !isNaN(creat)) ? calcClcr(age, poidsCalc, creat, state.sexe) : null; | var clcr = (!isNaN(age) && poidsCalc !== null && !isNaN(creat)) ? calcClcr(age, poidsCalc, creat, state.sexe) : null; | ||
return { clcr: clcr, imc: imc, pi: pi, poidsCalc: poidsCalc }; | |||
} | |||
var clcrColor = '#64748b'; | function renderResult() { | ||
var res = calcClcrFromState(); | |||
var clcr = res.clcr; var imc = res.imc; var pi = res.pi; var poidsCalc = res.poidsCalc; | |||
var clcrColor = '#64748b'; var clcrLabel = ''; | |||
if (clcr !== null) { | if (clcr !== null) { | ||
if (clcr >= 60) { clcrColor = '#16a34a'; clcrLabel = 'Normale / légèrement réduite'; } | if (clcr >= 60) { clcrColor = '#16a34a'; clcrLabel = 'Normale / légèrement réduite'; } | ||
| Ligne 424 : | Ligne 419 : | ||
} | } | ||
// | // Update ClCr result box | ||
var box = document.getElementById('calc-result-box'); | |||
if (box) { | |||
if (clcr !== null) { | |||
var html = '<div>'; | |||
html += '<div style="font-size:11px;color:#64748b;font-weight:600">' + (state.mode === 'direct' ? 'DFG (LABORATOIRE)' : 'CLAIRANCE CRÉATININE') + '</div>'; | |||
html += '<div class="calc-clcr-val" style="color:' + clcrColor + '">' + clcr.toFixed(1) + ' <span style="font-size:14px;font-weight:500">mL/min</span></div>'; | |||
html += '<div class="calc-clcr-label" style="color:' + clcrColor + '">' + clcrLabel + '</div>'; | |||
html += '</div>'; | |||
if (imc !== null) { | |||
html += '<div class="calc-imc-info">'; | |||
html += '<div style="font-size:11px;color:#64748b;font-weight:600">IMC</div>'; | |||
html += '<div style="font-size:20px;font-weight:700;color:#0f2d5e">' + imc.toFixed(1) + ' <span style="font-size:11px">kg/m²</span></div>'; | |||
if (imc >= 30 && pi) { | |||
html += '<div>IMC ≥ 30 — Poids de dosage: <b>' + poidsCalc.toFixed(1) + ' kg</b><br>Poids idéal: ' + pi.toFixed(1) + ' kg</div>'; | |||
} | |||
html += '</div>'; | |||
} | |||
box.innerHTML = html; | |||
box.style.display = 'flex'; | |||
box.style.border = '2px solid ' + clcrColor + '33'; | |||
} else { | |||
box.innerHTML = ''; | |||
box.style.display = 'none'; | |||
} | |||
} | |||
// Update sexe buttons | |||
var btnH = document.getElementById('calc-btn-h'); | |||
var btnF = document.getElementById('calc-btn-f'); | |||
if (btnH) btnH.className = 'calc-sexe-btn' + (state.sexe === 'H' ? ' active' : ''); | |||
if (btnF) btnF.className = 'calc-sexe-btn' + (state.sexe === 'F' ? ' active' : ''); | |||
// Update drug result | |||
renderDrugResult(clcr); | |||
} | |||
function renderDrugResult(clcr) { | |||
var drugData = getDrugData(state.selectedCat, state.selectedDrug); | |||
var section = document.getElementById('calc-drug-result'); | |||
if (!section) return; | |||
if (!state.selectedDrug || !drugData) { section.innerHTML = ''; return; } | |||
var html = '<div class="calc-card">'; | |||
html += '<h2>3. Posologie recommandée</h2>'; | |||
html += '<div style="font-size:13px;color:#64748b;margin-bottom:14px">' + state.selectedDrug + '</div>'; | |||
if (drugData.notes) html += '<div class="calc-note">⚠️ ' + drugData.notes + '</div>'; | |||
html += '<table class="calc-table"><thead><tr><th>ClCr (mL/min)</th><th>Posologie</th></tr></thead><tbody>'; | |||
var activeIdx = clcr !== null ? getActiveDose(drugData.doses, clcr) : -1; | |||
drugData.doses.forEach(function(d, i) { | |||
var isActive = clcr !== null && i === activeIdx; | |||
var rangeLabel = d.max >= 999 ? '≥ ' + d.min : d.min === 0 ? '< ' + (drugData.doses.filter(function(x){return x.min>0;}).map(function(x){return x.min;}).sort(function(a,b){return a-b;})[0]||5) : d.min + ' – ' + d.max; | |||
html += '<tr' + (isActive ? ' class="active-row"' : ' style="background:' + (i%2===0?'white':'#f8fafc') + '"') + '>'; | |||
html += '<td style="font-weight:600;white-space:nowrap">' + (isActive ? '▶ ' : '') + rangeLabel + '</td>'; | |||
html += '<td>' + d.label + (isActive ? '<span class="calc-badge">VOTRE PATIENT</span>' : '') + '</td>'; | |||
html += '</tr>'; | |||
}); | |||
html += '</tbody></table>'; | |||
if (clcr === null) html += '<div class="calc-hint">💡 Entrez les données du patient (étape 1) pour mettre en évidence la posologie appropriée.</div>'; | |||
html += '</div>'; | |||
section.innerHTML = html; | |||
} | |||
function renderDrugSelector() { | |||
var searchResults = []; | var searchResults = []; | ||
if (state.search && state.search.length >= 2) { | if (state.search && state.search.length >= 2) { | ||
| Ligne 435 : | Ligne 494 : | ||
} | } | ||
var | var sr = document.getElementById('calc-search-results'); | ||
var | if (sr) { | ||
var html = ''; | |||
searchResults.forEach(function(r) { | |||
html += '<button onclick="calcSelectDrug(\'' + r.cat.replace(/'/g, "\\'") + '\',\'' + r.drug.replace(/'/g, "\\'") + '\')">'; | |||
html += '<span style="color:#64748b;font-size:11px">' + r.cat + ' / </span>' + r.drug + '</button>'; | |||
}); | |||
sr.innerHTML = html; | |||
} | |||
var cats = document.getElementById('calc-cats'); | |||
if (cats) { | |||
var html = ''; | |||
Object.keys(ANTIBIOS).forEach(function(cat) { | |||
html += '<button class="calc-cat-btn ' + (state.selectedCat === cat ? 'active' : '') + '" onclick="calcSetCat(\'' + cat.replace(/'/g, "\\'") + '\')">' + cat + '</button>'; | |||
}); | |||
cats.innerHTML = html; | |||
} | |||
var drugs = document.getElementById('calc-drugs'); | |||
if (drugs) { | |||
var html = ''; | |||
if (state.selectedCat && ANTIBIOS[state.selectedCat]) { | |||
Object.keys(ANTIBIOS[state.selectedCat]).forEach(function(drug) { | |||
html += '<button class="calc-drug-btn ' + (state.selectedDrug === drug ? 'active' : '') + '" onclick="calcSetDrug(\'' + drug.replace(/'/g, "\\'") + '\')">' + drug + '</button>'; | |||
}); | |||
} | |||
drugs.innerHTML = html; | |||
} | |||
} | |||
function render() { | |||
var app = document.getElementById('calc-app'); | |||
if (!app) return; | |||
var html = ''; | var html = ''; | ||
html += '<div class="calc-header">'; | html += '<div class="calc-header">'; | ||
html += '<h1> | html += '<h1>Calculateur posologique — Insuffisance rénale</h1>'; | ||
html += '<p>Basé sur le guide APES (RPEI) — Octobre 2019 • Adultes seulement • Le jugement clinique doit prévaloir</p>'; | html += '<p>Basé sur le guide APES (RPEI) — Octobre 2019 • Adultes seulement • Le jugement clinique doit prévaloir</p>'; | ||
html += '</div>'; | html += '</div>'; | ||
html += '<div class="calc-card">'; | html += '<div class="calc-card">'; | ||
html += '<h2>1. | html += '<h2>1. Fonction rénale</h2>'; | ||
html += '<div style=" | |||
html += '< | // Mode toggle | ||
html += '<div style="display:flex;gap:8px;margin-bottom:16px;">'; | |||
html += '<button class="calc-sexe-btn ' + (state. | html += '<button id="calc-btn-cg" class="calc-sexe-btn ' + (state.mode === 'cg' ? 'active' : '') + '" onclick="calcSetMode(\'cg\')">Calculer (Cockcroft-Gault)</button>'; | ||
html += '<button id="calc-btn-direct" class="calc-sexe-btn ' + (state.mode === 'direct' ? 'active' : '') + '" onclick="calcSetMode(\'direct\')">DFG déjà connu</button>'; | |||
html += '</div>'; | html += '</div>'; | ||
if ( | if (state.mode === 'cg') { | ||
html += '<div class="calc- | html += '<div style="margin-bottom:12px;display:flex;align-items:center;gap:8px;">'; | ||
html += '<div>'; | html += '<span style="font-size:13px;color:#64748b;width:50px">Sexe</span>'; | ||
html += '<div | html += '<button id="calc-btn-h" class="calc-sexe-btn ' + (state.sexe === 'H' ? 'active' : '') + '" onclick="calcSetSexe(\'H\')">Homme</button>'; | ||
html += '<div | html += '<button id="calc-btn-f" class="calc-sexe-btn ' + (state.sexe === 'F' ? 'active' : '') + '" onclick="calcSetSexe(\'F\')">Femme</button>'; | ||
html += '<div | html += '</div>'; | ||
html += '<div class="calc-grid">'; | |||
html += '<div><label>Âge (ans)</label><input id="calc-age" type="number" placeholder="ex: 65" oninput="calcSetField(\'age\',this.value)"></div>'; | |||
html += '<div><label>Poids réel (kg)</label><input id="calc-poids" type="number" placeholder="ex: 70" oninput="calcSetField(\'poids\',this.value)"></div>'; | |||
html += '<div><label>Taille (cm)</label><input id="calc-taille" type="number" placeholder="ex: 170" oninput="calcSetField(\'taille\',this.value)"></div>'; | |||
html += '<div><label>Créatinine (μmol/L)</label><input id="calc-creat" type="number" placeholder="ex: 120" oninput="calcSetField(\'creat\',this.value)"></div>'; | |||
html += '</div>'; | html += '</div>'; | ||
} else { | |||
html += '<div style="max-width:250px;">'; | |||
html += '<label style="font-size:11px;color:#64748b;font-weight:600;display:block;margin-bottom:4px;">DFG / ClCr (mL/min)</label>'; | |||
html += '<input id="calc-dfg-direct" type="number" placeholder="ex: 45" style="width:100%;padding:10px 14px;border-radius:8px;border:1.5px solid #e2e8f0;font-size:18px;font-weight:700;color:#0f2d5e;outline:none;" oninput="calcSetField(\'dfgDirect\',this.value)">'; | |||
html += '<div style="font-size:11px;color:#64748b;margin-top:6px;">Valeur fournie par le laboratoire</div>'; | |||
html += '</div>'; | html += '</div>'; | ||
} | } | ||
html += '<div id="calc-result-box" class="calc-result-box" style="display:none"></div>'; | |||
html += '</div>'; | html += '</div>'; | ||
html += '<div class="calc-card">'; | html += '<div class="calc-card">'; | ||
html += '<h2>2. Sélection de l\'antimicrobien</h2>'; | html += '<h2>2. Sélection de l\'antimicrobien</h2>'; | ||
html += '<input class="calc-search" type="text" | html += '<input id="calc-search-input" class="calc-search" type="text" placeholder="🔍 Rechercher un antimicrobien..." oninput="calcSetSearch(this.value)">'; | ||
html += '<div id="calc-search-results" class="calc-search-results"></div>'; | |||
html += '<div id="calc-cats" class="calc-cats"></div>'; | |||
html += '<div id="calc-drugs" class="calc-drugs"></div>'; | |||
html += '<div | |||
html += '</div>'; | html += '</div>'; | ||
html += '<div id="calc-drug-result"></div>'; | |||
html += '<div class="calc-warning">⚠️ <b>Avertissement clinique :</b> Cet outil est basé sur le guide APES (RPEI, octobre 2019). Les ajustements servent uniquement à guider la décision clinique — le jugement clinique doit prévaloir. Consulter un pharmacien pour les cas complexes.</div>'; | html += '<div class="calc-warning">⚠️ <b>Avertissement clinique :</b> Cet outil est basé sur le guide APES (RPEI, octobre 2019). Les ajustements servent uniquement à guider la décision clinique — le jugement clinique doit prévaloir. Consulter un pharmacien pour les cas complexes.</div>'; | ||
app.innerHTML = html; | app.innerHTML = html; | ||
renderResult(); | |||
renderDrugSelector(); | |||
} | } | ||
window.calcSetMode = function(mode) { state.mode = mode; render(); }; | |||
window.calcSetField = function(field, val) { | window.calcSetField = function(field, val) { | ||
state[field] = val; | |||
renderResult(); | |||
}; | |||
window.calcSetSexe = function(sexe) { state.sexe = sexe; renderResult(); }; | |||
window.calcSetSearch = function(val) { state.search = val; state.selectedCat = ''; state.selectedDrug = ''; renderDrugSelector(); renderDrugResult(calcClcrFromState().clcr); }; | |||
window.calcSetCat = function(cat) { state.selectedCat = cat; state.selectedDrug = ''; renderDrugSelector(); renderDrugResult(calcClcrFromState().clcr); }; | |||
window.calcSetDrug = function(drug) { state.selectedDrug = drug; renderDrugSelector(); renderDrugResult(calcClcrFromState().clcr); }; | |||
window.calcSelectDrug = function(cat, drug) { state.selectedCat = cat; state.selectedDrug = drug; state.search = ''; var si = document.getElementById('calc-search-input'); if(si) si.value=''; renderDrugSelector(); renderDrugResult(calcClcrFromState().clcr); }; | |||
}; | |||
window.calcSetSexe = function(sexe) { state.sexe = sexe; | |||
window.calcSetSearch = function(val) { state.search = val; state.selectedCat = ''; state.selectedDrug = ''; | |||
window.calcSetCat = function(cat) { state.selectedCat = cat; state.selectedDrug = ''; | |||
window.calcSetDrug = function(drug) { state.selectedDrug = drug; | |||
window.calcSelectDrug = function(cat, drug) { state.selectedCat = cat; state.selectedDrug = drug; state.search = ''; | |||
if (document.readyState === 'loading') { | if (document.readyState === 'loading') { | ||
Dernière version du 9 mars 2026 à 22:21
// Forcer l'ouverture des sections hidden dans citizen qui les cachent par défaut
function ouvrirSections() {
document.querySelectorAll('.citizen-section').forEach(function (el) {
el.removeAttribute('hidden');
});
}
document.addEventListener('DOMContentLoaded', ouvrirSections);
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
ouvrirSections();
}
});
new MutationObserver(ouvrirSections).observe(document.documentElement, { childList: true, subtree: true });
// ============================================
// CALCULATEUR IRC — Microguide
// ============================================
if (mw.config.get('wgPageName') === 'Calculateur') {
var style = document.createElement('style');
style.textContent = `
#calc-app * { box-sizing: border-box; font-family: 'Segoe UI', system-ui, sans-serif; }
#calc-app { background: #f0f4f8; padding: 16px; border-radius: 12px; }
.calc-header { background: linear-gradient(135deg, #1a5fa8 0%, #2980d4 100%); padding: 18px 22px; border-radius: 12px; color: white; margin-bottom: 16px; }
.calc-header h1 { margin: 0; font-size: 20px; font-weight: 700; }
.calc-header p { margin: 4px 0 0; font-size: 12px; opacity: 0.75; }
.calc-card { background: white; border-radius: 12px; padding: 20px 22px; margin-bottom: 14px; box-shadow: 0 2px 8px rgba(0,0,0,0.06); border: 1px solid #e2e8f0; }
.calc-card h2 { margin: 0 0 14px; font-size: 15px; font-weight: 700; color: #0f2d5e; }
.calc-grid { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 10px; margin-bottom: 12px; }
.calc-grid label { font-size: 11px; color: #64748b; font-weight: 600; display: block; margin-bottom: 4px; }
.calc-grid input { width: 100%; padding: 8px 10px; border-radius: 8px; border: 1.5px solid #e2e8f0; font-size: 14px; color: #0f2d5e; font-weight: 600; outline: none; }
.calc-sexe-btn { padding: 6px 18px; border-radius: 8px; border: 1.5px solid #e2e8f0; background: white; color: #64748b; font-weight: 600; cursor: pointer; font-size: 13px; margin-right: 8px; }
.calc-sexe-btn.active { border-color: #0f2d5e; background: #0f2d5e; color: white; }
.calc-result-box { background: #f8fafc; border-radius: 10px; padding: 14px 18px; display: flex; align-items: center; gap: 20px; flex-wrap: wrap; margin-top: 10px; }
.calc-clcr-val { font-size: 32px; font-weight: 800; line-height: 1; }
.calc-clcr-label { font-size: 12px; font-weight: 600; margin-top: 2px; }
.calc-search { width: 100%; padding: 10px 14px; border-radius: 10px; border: 1.5px solid #e2e8f0; font-size: 14px; margin-bottom: 10px; outline: none; }
.calc-cats { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.calc-cat-btn { padding: 6px 14px; border-radius: 20px; border: 1.5px solid #e2e8f0; background: white; color: #475569; font-weight: 600; cursor: pointer; font-size: 12px; }
.calc-cat-btn.active { border-color: #0f2d5e; background: #0f2d5e; color: white; }
.calc-drugs { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.calc-drug-btn { padding: 6px 12px; border-radius: 8px; border: 1.5px solid #e2e8f0; background: white; color: #475569; font-weight: 500; cursor: pointer; font-size: 12px; }
.calc-drug-btn.active { border-color: #1a9d8f; background: #e8f6f4; color: #0f7a6e; font-weight: 700; }
.calc-table { width: 100%; border-collapse: collapse; font-size: 13px; margin-top: 10px; }
.calc-table th { background: #f1f5f9; padding: 8px 12px; text-align: left; font-weight: 700; color: #0f2d5e; }
.calc-table td { padding: 10px 12px; border-bottom: 1px solid #f1f5f9; }
.calc-table tr.active-row { background: #e8f4f0 !important; outline: 2px solid #1a9d8f; }
.calc-table tr.active-row td { color: #0a5a52; font-weight: 700; }
.calc-badge { background: #1a9d8f; color: white; font-size: 10px; padding: 2px 6px; border-radius: 4px; margin-left: 8px; font-weight: 700; }
.calc-note { background: #fefce8; border: 1px solid #fde68a; border-radius: 8px; padding: 10px 14px; font-size: 12px; color: #92400e; margin-bottom: 12px; }
.calc-warning { background: #fef2f2; border: 1px solid #fecaca; border-radius: 10px; padding: 12px 16px; font-size: 11px; color: #991b1b; margin-top: 14px; }
.calc-search-results button { display: block; width: 100%; text-align: left; padding: 8px 12px; margin-bottom: 4px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; cursor: pointer; font-size: 13px; color: #0f2d5e; }
.calc-hint { margin-top: 12px; padding: 10px 14px; background: #f0f9ff; border-radius: 8px; font-size: 12px; color: #0369a1; }
.calc-imc-info { border-left: 1px solid #e2e8f0; padding-left: 16px; font-size: 11px; color: #d97706; }
@media (max-width: 600px) { .calc-grid { grid-template-columns: 1fr 1fr; } }
`;
document.head.appendChild(style);
var ANTIBIOS = {
"Céphalosporines": {
"Céfadroxil (PO)": { notes: "", doses: [
{ min: 30, max: 999, label: "500 mg PO q12h ou 1g PO q12-24h" },
{ min: 20, max: 29, label: "1g PO x1 dose puis 500 mg PO q12h" },
{ min: 10, max: 19, label: "1g PO x1 dose puis 500 mg PO q24h" },
{ min: 0, max: 9, label: "1g PO x1 dose puis 500 mg PO q24-36h" },
]},
"Céfazoline (IV)": { notes: "", doses: [
{ min: 35, max: 999, label: "1-2g IV q8h (fréq. max. q6h)" },
{ min: 11, max: 34, label: "1-2g IV q12h" },
{ min: 0, max: 10, label: "1g IV q24h" },
]},
"Céfépime (IV) — IIA/IU": { notes: "Infections intra-abdominales et urinaires", doses: [
{ min: 30, max: 999, label: "1-2g IV q12h" },
{ min: 11, max: 29, label: "1-2g IV q24h" },
{ min: 6, max: 10, label: "1g IV q24h" },
{ min: 0, max: 5, label: "500 mg IV q24h" },
]},
"Céfépime (IV) — NF": { notes: "Neutropénie fébrile", doses: [
{ min: 30, max: 999, label: "2g IV q8h" },
{ min: 11, max: 29, label: "2g IV q12h" },
{ min: 6, max: 10, label: "2g IV q24h" },
{ min: 0, max: 5, label: "1g IV q24h" },
]},
"Céfixime (PO)": { notes: "", doses: [
{ min: 21, max: 999, label: "400 mg PO q24h" },
{ min: 11, max: 20, label: "300 mg (¾ co.) PO q24h" },
{ min: 0, max: 10, label: "200 mg PO q24h" },
]},
"Céfotaxime (IV)": { notes: "", doses: [
{ min: 21, max: 999, label: "1-2g IV q6-8h (jusqu'à 2g IV q4h si SNC)" },
{ min: 0, max: 20, label: "Diminuer la dose de 50%. Certains: 2g IV q12-24h" },
]},
"Céfoxitine (IV)": { notes: "Dose max: 12g/jour", doses: [
{ min: 30, max: 999, label: "1-2g IV q6-8h" },
{ min: 11, max: 29, label: "1-2g IV q8-12h" },
{ min: 5, max: 10, label: "1-2g IV q12-24h" },
{ min: 1, max: 4, label: "50% de la dose IV q12-24h" },
{ min: 0, max: 0, label: "50% de la dose IV q24-48h" },
]},
"Cefprozil (PO)": { notes: "", doses: [
{ min: 31, max: 999, label: "250-500 mg PO q12h" },
{ min: 0, max: 30, label: "250 mg PO q12h" },
]},
"Ceftazidime (IV)": { notes: "", doses: [
{ min: 31, max: 999, label: "1-2g IV q8h" },
{ min: 16, max: 30, label: "1-2g IV q12h" },
{ min: 6, max: 15, label: "1-2g IV q24h" },
{ min: 1, max: 5, label: "500mg-1g IV q24h" },
{ min: 0, max: 0, label: "500mg-1g IV q48h" },
]},
"Ceftobiprole (IV)": { notes: "Perfusion sur 4h si Clcr > 150 mL/min", doses: [
{ min: 31, max: 999, label: "500 mg IV q8h perf. sur 2h" },
{ min: 16, max: 30, label: "500 mg IV q12h perf. sur 2h" },
{ min: 6, max: 15, label: "250 mg IV q12h perf. sur 2h" },
{ min: 0, max: 5, label: "250 mg IV q24h perf. sur 2h" },
]},
"Ceftriaxone (IV)": { notes: "Aucun ajustement requis sauf IR + IH (dose max: 2g/jour)", doses: [
{ min: 0, max: 999, label: "1-2g IV q24h (max: 2g IV q12h pour SNC)" },
]},
"Céfuroxime axétil (PO)": { notes: "", doses: [
{ min: 11, max: 999, label: "250-500 mg PO q12h" },
{ min: 0, max: 10, label: "250 mg PO q24h" },
]},
"Céfuroxime (IV)": { notes: "", doses: [
{ min: 21, max: 999, label: "750mg-1,5g IV q8h" },
{ min: 11, max: 20, label: "750 mg IV q12h" },
{ min: 0, max: 10, label: "750 mg IV q24h" },
]},
"Céphalexine (PO)": { notes: "", doses: [
{ min: 41, max: 999, label: "250mg-1g PO q6h" },
{ min: 11, max: 40, label: "250-500 mg PO q8-12h" },
{ min: 0, max: 10, label: "250-500 mg PO q12-24h" },
]},
},
"Carbapénèmes": {
"Ertapénem (IV)": { notes: "", doses: [
{ min: 31, max: 999, label: "1g IV q24h" },
{ min: 0, max: 30, label: "500 mg IV q24h" },
]},
"Imipénem-Cilastatine (IV)": { notes: "PR ≥ 70 kg: 500 mg IV q6h (max 4g/jour). PR < 70 kg: voir monographie.", doses: [
{ min: 71, max: 999, label: "500 mg IV q6h (max: 4g/jour)" },
{ min: 41, max: 70, label: "500 mg IV q6-8h" },
{ min: 21, max: 40, label: "500 mg IV q8-12h" },
{ min: 6, max: 20, label: "250 mg IV q12h" },
{ min: 0, max: 5, label: "⚠️ Non recommandé" },
]},
"Méropénem (IV) — usuel": { notes: "Posologie usuelle", doses: [
{ min: 26, max: 999, label: "1g IV q8h" },
{ min: 11, max: 25, label: "1g IV q12h" },
{ min: 6, max: 10, label: "500 mg IV q12h" },
{ min: 0, max: 5, label: "500 mg IV q24h" },
]},
"Méropénem (IV) — SNC": { notes: "Infections du SNC", doses: [
{ min: 26, max: 999, label: "2g IV q8h" },
{ min: 11, max: 25, label: "2g IV q12h" },
{ min: 6, max: 10, label: "1g IV q12h" },
{ min: 0, max: 5, label: "1g IV q24h" },
]},
},
"Macrolides": {
"Azithromycine (PO)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "250-500 mg PO q24h" }]},
"Azithromycine (IV)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "500 mg IV q24h" }]},
"Clarithromycine (PO)": { notes: "", doses: [
{ min: 31, max: 999, label: "250-500 mg PO q12h" },
{ min: 0, max: 30, label: "250 mg PO q24h (ou q12h si infection sévère)" },
]},
"Érythromycine (PO/IV)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "Posologie selon indication et formulation" }]},
},
"Pénicillines": {
"Amoxicilline (PO)": { notes: "", doses: [
{ min: 31, max: 999, label: "250mg-1g PO q8h" },
{ min: 11, max: 30, label: "250-500 mg PO q12h" },
{ min: 0, max: 10, label: "250-500 mg PO q24h" },
]},
"Amoxicilline-Clavulanate (PO)": { notes: "", doses: [
{ min: 31, max: 999, label: "500/125 mg PO q8h ou 875/125 mg PO q12h" },
{ min: 11, max: 30, label: "500/125 mg PO q12h" },
{ min: 0, max: 10, label: "500/125 mg PO q24h" },
]},
"Ampicilline (IV)": { notes: "Posologie usuelle: 1-2g IV q4-6h", doses: [
{ min: 41, max: 999, label: "1-2g IV q6h" },
{ min: 31, max: 40, label: "1-2g IV q6-8h" },
{ min: 16, max: 30, label: "1-2g IV q8-12h" },
{ min: 0, max: 15, label: "1-2g IV q12-24h" },
]},
"Cloxacilline (PO)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "250-500 mg PO q6h (jusqu'à 4g/jour)" }]},
"Cloxacilline (IV)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "1-2g IV q4-6h" }]},
"Pénicilline G sodique (IV)": { notes: "DC = dose de charge", doses: [
{ min: 21, max: 999, label: "1-4 mU IV q4-6h" },
{ min: 11, max: 20, label: "DC suivie de 50% de la dose IV q4-6h" },
{ min: 0, max: 10, label: "DC suivie de 50% de la dose IV q8-12h" },
]},
"Pipéracilline-Tazobactam (IV) — usuel": { notes: "Posologie usuelle", doses: [
{ min: 41, max: 999, label: "3,375g [3g/0,375g] IV q6h ou 4,5g [4g/0,5g] IV q8h" },
{ min: 21, max: 40, label: "2,25g [2g/0,25g] IV q6h" },
{ min: 0, max: 20, label: "2,25g [2g/0,25g] IV q8h" },
]},
"Pipéracilline-Tazobactam (IV) — PAH/NF": { notes: "Pneumonie hospitalière ou neutropénie fébrile", doses: [
{ min: 41, max: 999, label: "4,5g [4g/0,5g] IV q6h" },
{ min: 21, max: 40, label: "3,375g [3g/0,375g] IV q6h" },
{ min: 0, max: 20, label: "2,25g [2g/0,25g] IV q6h" },
]},
},
"Fluoroquinolones": {
"Ciprofloxacine (PO)": { notes: "", doses: [
{ min: 31, max: 999, label: "500-750 mg PO q12h" },
{ min: 16, max: 30, label: "500 mg PO q12h" },
{ min: 0, max: 15, label: "500 mg PO q24h ou 250 mg PO q12h" },
]},
"Ciprofloxacine (IV)": { notes: "", doses: [
{ min: 31, max: 999, label: "400 mg IV q8-12h" },
{ min: 16, max: 30, label: "400 mg IV q12h" },
{ min: 0, max: 15, label: "400 mg IV q24h ou 200 mg IV q12h" },
]},
"Lévofloxacine — légère/prostatite": { notes: "Ex: prostatite chronique", doses: [
{ min: 51, max: 999, label: "500 mg PO/IV q24h" },
{ min: 21, max: 50, label: "500 mg PO/IV x1 dose, puis 250 mg PO/IV q24h" },
{ min: 0, max: 20, label: "500 mg PO/IV x1 dose, puis 250 mg PO/IV q48h" },
]},
"Lévofloxacine — PAH": { notes: "Pneumonie hospitalière", doses: [
{ min: 51, max: 999, label: "750 mg PO/IV q24h" },
{ min: 21, max: 50, label: "750 mg PO/IV q48h" },
{ min: 0, max: 20, label: "750 mg PO/IV x1 dose, puis 500 mg PO/IV q48h" },
]},
"Moxifloxacine (PO/IV)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "400 mg PO/IV q24h" }]},
"Norfloxacine (PO) — IU": { notes: "Infection urinaire", doses: [
{ min: 31, max: 999, label: "400 mg PO q12-24h" },
{ min: 0, max: 30, label: "400 mg PO q24h" },
]},
},
"Autres antibactériens": {
"Clindamycine (PO)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "150-450 mg PO q6h" }]},
"Clindamycine (IV)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "600-900 mg IV q8h (fréq. max.: q6h)" }]},
"Daptomycine (IV) — usuelle": { notes: "Pour dose 6 mg/kg: utiliser PD si PR > 140 kg", doses: [
{ min: 31, max: 999, label: "4-6 mg/kg PR IV q24h" },
{ min: 0, max: 30, label: "Même dose IV q48h" },
]},
"Doxycycline (PO)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "100 mg PO q12h" }]},
"Linézolide (PO/IV)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "600 mg PO/IV q12h" }]},
"Métronidazole (PO)": { notes: "", doses: [
{ min: 11, max: 999, label: "250-500 mg PO q8h ou 500 mg PO q12h" },
{ min: 0, max: 10, label: "500 mg PO q12h" },
]},
"Métronidazole (IV)": { notes: "", doses: [
{ min: 11, max: 999, label: "500 mg IV q8h (fréq. max.: q6h)" },
{ min: 0, max: 10, label: "500 mg IV q12h" },
]},
"Nitrofurantoïne (PO)": { notes: "Cystite non compliquée uniquement", doses: [
{ min: 46, max: 999, label: "50-100 mg PO QID ou 100 mg PO q12h (monohydrate)" },
{ min: 31, max: 45, label: "Données limitées. Possiblement efficace à court terme." },
{ min: 0, max: 30, label: "⚠️ Contre-indiqué — risque de toxicité accru" },
]},
"Tétracycline (PO)": { notes: "", doses: [
{ min: 51, max: 999, label: "250-500 mg PO q6h" },
{ min: 11, max: 50, label: "250-500 mg PO q12h" },
{ min: 0, max: 10, label: "250-500 mg PO q24h" },
]},
"Tigécycline (IV)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "100 mg IV x1 dose puis 50 mg IV q12h" }]},
"Triméthoprime (PO)": { notes: "", doses: [
{ min: 16, max: 999, label: "100 mg PO q12h" },
{ min: 6, max: 15, label: "50 mg PO q12h" },
{ min: 0, max: 5, label: "⚠️ Non recommandé" },
]},
"TMP-SMX — usuel": { notes: "80 mg TMP = 1 co SS; 160 mg TMP = 1 co DS", doses: [
{ min: 31, max: 999, label: "160-320 mg TMP PO/IV q12h" },
{ min: 16, max: 30, label: "80 mg TMP PO/IV q12h ou 160 mg TMP PO/IV q24h" },
{ min: 0, max: 15, label: "⚠️ Non recommandé" },
]},
"Vancomycine (IV)": { notes: "⚠️ Amorcer empiriquement selon âge, fonction rénale et poids puis ajuster selon dosages sériques. DC = 25 mg/kg PR. DM: 15-20 mg/kg PD/dose q8-12h (max 2g/dose). Consulter pharmacien.", doses: [
{ min: 71, max: 999, label: "DM IV q12h — surveiller dosages sériques" },
{ min: 41, max: 70, label: "DM IV q24h — surveiller dosages sériques" },
{ min: 21, max: 40, label: "DM IV q36-48h — dosages sériques quotidiens" },
{ min: 11, max: 20, label: "DM IV q72-96h" },
{ min: 0, max: 10, label: "DM IV q5-7 jours — consulter pharmacien" },
]},
"Vancomycine (PO)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "125 mg PO QID (max 500 mg PO QID si infection compliquée)" }]},
},
"Antifongiques": {
"Anidulafungine (IV)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "200 mg IV x1 dose puis 100 mg IV q24h" }]},
"Caspofungine (IV)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "70 mg IV x1 dose puis 50 mg IV q24h" }]},
"Fluconazole (PO/IV)": { notes: "DC = 100% de la dose. Écart: 100-800 mg PO/IV q24h", doses: [
{ min: 51, max: 999, label: "DC puis 100% dose PO/IV q24h" },
{ min: 11, max: 50, label: "DC puis 50% de la dose PO/IV q24h" },
{ min: 0, max: 10, label: "DC puis 25-50% de la dose PO/IV q24h" },
]},
"Isavuconazole (PO/IV)": { notes: "Aucun ajustement requis. 372 mg sulfate = 200 mg isavuconazole.", doses: [{ min: 0, max: 999, label: "DC: 200 mg PO/IV q8h x6 doses, puis 200 mg PO/IV q24h" }]},
"Kétoconazole (PO)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "200-400 mg PO q24h" }]},
"Micafungine — prophylaxie": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "50-100 mg IV q24h" }]},
"Micafungine — traitement": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "100-150 mg IV q24h" }]},
"Terbinafine (PO)": { notes: "", doses: [
{ min: 51, max: 999, label: "250 mg PO q24h" },
{ min: 0, max: 50, label: "⚠️ Non recommandé — données limitées" },
]},
},
"Antimycobactériens": {
"Ethambutol (PO)": { notes: "Si PR > 120% PI, baser dose sur PI.", doses: [
{ min: 31, max: 999, label: "15-25 mg/kg PO q24h" },
{ min: 0, max: 30, label: "15-25 mg/kg/dose PO DIE 3 jours/semaine" },
]},
"Isoniazide (PO)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "5 mg/kg PO q24h (max: 300 mg)" }]},
"Pyrazinamide (PO)": { notes: "Si PR > 120% PI, baser dose sur PI.", doses: [
{ min: 31, max: 999, label: "20-25 mg/kg PO q24h (max: 2g)" },
{ min: 0, max: 30, label: "25-35 mg/kg/dose PO DIE 3 jours/semaine" },
]},
"Rifabutine (PO)": { notes: "", doses: [
{ min: 16, max: 999, label: "300 mg PO q24h" },
{ min: 0, max: 15, label: "150 mg PO q24h" },
]},
"Rifampicine (PO)": { notes: "Aucun ajustement requis", doses: [{ min: 0, max: 999, label: "10 mg/kg PO q24h (max: 600 mg)" }]},
},
"Antiviraux": {
"Acyclovir (IV)": { notes: "Posologie usuelle: 5-15 mg/kg/dose IV q8h", doses: [
{ min: 26, max: 999, label: "100% de la dose IV q8h" },
{ min: 11, max: 25, label: "100% de la dose IV q12h" },
{ min: 6, max: 10, label: "100% de la dose IV q24h" },
{ min: 0, max: 5, label: "50% de la dose IV q24h" },
]},
"Acyclovir (PO) — zona": { notes: "", doses: [
{ min: 26, max: 999, label: "800 mg PO q4h (5 fois/jour)" },
{ min: 11, max: 25, label: "800 mg PO q8h" },
{ min: 0, max: 10, label: "800 mg PO q12h" },
]},
"Famciclovir (PO) — zona": { notes: "", doses: [
{ min: 41, max: 999, label: "500 mg PO q8h" },
{ min: 21, max: 40, label: "500 mg PO q12h" },
{ min: 6, max: 20, label: "500 mg PO q24h" },
{ min: 0, max: 5, label: "250 mg PO q24h" },
]},
"Oseltamivir (PO) — traitement": { notes: "Influenza A et B", doses: [
{ min: 31, max: 999, label: "75 mg PO q12h" },
{ min: 11, max: 30, label: "30 mg PO q12h" },
{ min: 6, max: 10, label: "30 mg PO q24h" },
{ min: 0, max: 5, label: "75 mg PO x1 dose" },
]},
"Oseltamivir (PO) — prophylaxie": { notes: "Prophylaxie Influenza A et B", doses: [
{ min: 31, max: 999, label: "75 mg PO q24h" },
{ min: 11, max: 30, label: "30 mg PO q24h" },
{ min: 6, max: 10, label: "30 mg PO q48h" },
{ min: 0, max: 5, label: "Privilégier zanamivir inhalation" },
]},
"Valacyclovir (PO) — zona": { notes: "", doses: [
{ min: 31, max: 999, label: "1g PO q8h" },
{ min: 16, max: 30, label: "1g PO q12h" },
{ min: 6, max: 15, label: "1g PO q24h" },
{ min: 0, max: 5, label: "500 mg PO q24h" },
]},
"Valacyclovir (PO) — herpès génital": { notes: "", doses: [
{ min: 31, max: 999, label: "1g PO q12h" },
{ min: 16, max: 30, label: "1g PO q24h" },
{ min: 0, max: 15, label: "500 mg PO q24h" },
]},
},
};
function calcClcr(age, poids, creat, sexe) {
var k = sexe === 'F' ? 0.85 : 1.0;
return ((140 - age) * poids / (49 * creat)) * 60 * k;
}
function calcPI(taille, sexe) {
return sexe === 'F' ? 45.5 + 0.91 * (taille - 152) : 50 + 0.91 * (taille - 152);
}
function calcIMC(poids, taille) {
return poids / Math.pow(taille / 100, 2);
}
var state = {
age: '', poids: '', taille: '', creat: '', sexe: 'H',
selectedCat: '', selectedDrug: '', search: '',
mode: 'cg', dfgDirect: ''
};
function getDrugData(cat, drug) {
return ANTIBIOS[cat] && ANTIBIOS[cat][drug] ? ANTIBIOS[cat][drug] : null;
}
function getActiveDose(doses, clcr) {
var clcrRounded = Math.round(clcr * 10) / 10;
for (var i = 0; i < doses.length; i++) {
if (clcrRounded >= doses[i].min && clcrRounded <= doses[i].max) return i;
}
for (var i = doses.length - 1; i >= 0; i--) {
if (clcrRounded >= doses[i].min) return i;
}
return doses.length - 1;
}
function calcClcrFromState() {
if (state.mode === 'direct') {
var dfg = parseFloat(state.dfgDirect);
return { clcr: isNaN(dfg) ? null : dfg, imc: null, pi: null, poidsCalc: null };
}
var age = parseFloat(state.age);
var poids = parseFloat(state.poids);
var taille = parseFloat(state.taille);
var creat = parseFloat(state.creat);
var imc = (!isNaN(poids) && !isNaN(taille)) ? calcIMC(poids, taille) : null;
var pi = (!isNaN(taille)) ? calcPI(taille, state.sexe) : null;
var poidsCalc = null;
if (!isNaN(poids) && !isNaN(taille)) {
poidsCalc = imc < 30 ? poids : pi + 0.4 * (poids - pi);
}
var clcr = (!isNaN(age) && poidsCalc !== null && !isNaN(creat)) ? calcClcr(age, poidsCalc, creat, state.sexe) : null;
return { clcr: clcr, imc: imc, pi: pi, poidsCalc: poidsCalc };
}
function renderResult() {
var res = calcClcrFromState();
var clcr = res.clcr; var imc = res.imc; var pi = res.pi; var poidsCalc = res.poidsCalc;
var clcrColor = '#64748b'; var clcrLabel = '';
if (clcr !== null) {
if (clcr >= 60) { clcrColor = '#16a34a'; clcrLabel = 'Normale / légèrement réduite'; }
else if (clcr >= 30) { clcrColor = '#d97706'; clcrLabel = 'IRC modérée'; }
else if (clcr >= 15) { clcrColor = '#ea580c'; clcrLabel = 'IRC sévère'; }
else { clcrColor = '#dc2626'; clcrLabel = 'IRC très sévère / terminale'; }
}
// Update ClCr result box
var box = document.getElementById('calc-result-box');
if (box) {
if (clcr !== null) {
var html = '<div>';
html += '<div style="font-size:11px;color:#64748b;font-weight:600">' + (state.mode === 'direct' ? 'DFG (LABORATOIRE)' : 'CLAIRANCE CRÉATININE') + '</div>';
html += '<div class="calc-clcr-val" style="color:' + clcrColor + '">' + clcr.toFixed(1) + ' <span style="font-size:14px;font-weight:500">mL/min</span></div>';
html += '<div class="calc-clcr-label" style="color:' + clcrColor + '">' + clcrLabel + '</div>';
html += '</div>';
if (imc !== null) {
html += '<div class="calc-imc-info">';
html += '<div style="font-size:11px;color:#64748b;font-weight:600">IMC</div>';
html += '<div style="font-size:20px;font-weight:700;color:#0f2d5e">' + imc.toFixed(1) + ' <span style="font-size:11px">kg/m²</span></div>';
if (imc >= 30 && pi) {
html += '<div>IMC ≥ 30 — Poids de dosage: <b>' + poidsCalc.toFixed(1) + ' kg</b><br>Poids idéal: ' + pi.toFixed(1) + ' kg</div>';
}
html += '</div>';
}
box.innerHTML = html;
box.style.display = 'flex';
box.style.border = '2px solid ' + clcrColor + '33';
} else {
box.innerHTML = '';
box.style.display = 'none';
}
}
// Update sexe buttons
var btnH = document.getElementById('calc-btn-h');
var btnF = document.getElementById('calc-btn-f');
if (btnH) btnH.className = 'calc-sexe-btn' + (state.sexe === 'H' ? ' active' : '');
if (btnF) btnF.className = 'calc-sexe-btn' + (state.sexe === 'F' ? ' active' : '');
// Update drug result
renderDrugResult(clcr);
}
function renderDrugResult(clcr) {
var drugData = getDrugData(state.selectedCat, state.selectedDrug);
var section = document.getElementById('calc-drug-result');
if (!section) return;
if (!state.selectedDrug || !drugData) { section.innerHTML = ''; return; }
var html = '<div class="calc-card">';
html += '<h2>3. Posologie recommandée</h2>';
html += '<div style="font-size:13px;color:#64748b;margin-bottom:14px">' + state.selectedDrug + '</div>';
if (drugData.notes) html += '<div class="calc-note">⚠️ ' + drugData.notes + '</div>';
html += '<table class="calc-table"><thead><tr><th>ClCr (mL/min)</th><th>Posologie</th></tr></thead><tbody>';
var activeIdx = clcr !== null ? getActiveDose(drugData.doses, clcr) : -1;
drugData.doses.forEach(function(d, i) {
var isActive = clcr !== null && i === activeIdx;
var rangeLabel = d.max >= 999 ? '≥ ' + d.min : d.min === 0 ? '< ' + (drugData.doses.filter(function(x){return x.min>0;}).map(function(x){return x.min;}).sort(function(a,b){return a-b;})[0]||5) : d.min + ' – ' + d.max;
html += '<tr' + (isActive ? ' class="active-row"' : ' style="background:' + (i%2===0?'white':'#f8fafc') + '"') + '>';
html += '<td style="font-weight:600;white-space:nowrap">' + (isActive ? '▶ ' : '') + rangeLabel + '</td>';
html += '<td>' + d.label + (isActive ? '<span class="calc-badge">VOTRE PATIENT</span>' : '') + '</td>';
html += '</tr>';
});
html += '</tbody></table>';
if (clcr === null) html += '<div class="calc-hint">💡 Entrez les données du patient (étape 1) pour mettre en évidence la posologie appropriée.</div>';
html += '</div>';
section.innerHTML = html;
}
function renderDrugSelector() {
var searchResults = [];
if (state.search && state.search.length >= 2) {
var q = state.search.toLowerCase();
Object.keys(ANTIBIOS).forEach(function(cat) {
Object.keys(ANTIBIOS[cat]).forEach(function(drug) {
if (drug.toLowerCase().indexOf(q) !== -1) searchResults.push({ cat: cat, drug: drug });
});
});
}
var sr = document.getElementById('calc-search-results');
if (sr) {
var html = '';
searchResults.forEach(function(r) {
html += '<button onclick="calcSelectDrug(\'' + r.cat.replace(/'/g, "\\'") + '\',\'' + r.drug.replace(/'/g, "\\'") + '\')">';
html += '<span style="color:#64748b;font-size:11px">' + r.cat + ' / </span>' + r.drug + '</button>';
});
sr.innerHTML = html;
}
var cats = document.getElementById('calc-cats');
if (cats) {
var html = '';
Object.keys(ANTIBIOS).forEach(function(cat) {
html += '<button class="calc-cat-btn ' + (state.selectedCat === cat ? 'active' : '') + '" onclick="calcSetCat(\'' + cat.replace(/'/g, "\\'") + '\')">' + cat + '</button>';
});
cats.innerHTML = html;
}
var drugs = document.getElementById('calc-drugs');
if (drugs) {
var html = '';
if (state.selectedCat && ANTIBIOS[state.selectedCat]) {
Object.keys(ANTIBIOS[state.selectedCat]).forEach(function(drug) {
html += '<button class="calc-drug-btn ' + (state.selectedDrug === drug ? 'active' : '') + '" onclick="calcSetDrug(\'' + drug.replace(/'/g, "\\'") + '\')">' + drug + '</button>';
});
}
drugs.innerHTML = html;
}
}
function render() {
var app = document.getElementById('calc-app');
if (!app) return;
var html = '';
html += '<div class="calc-header">';
html += '<h1>Calculateur posologique — Insuffisance rénale</h1>';
html += '<p>Basé sur le guide APES (RPEI) — Octobre 2019 • Adultes seulement • Le jugement clinique doit prévaloir</p>';
html += '</div>';
html += '<div class="calc-card">';
html += '<h2>1. Fonction rénale</h2>';
// Mode toggle
html += '<div style="display:flex;gap:8px;margin-bottom:16px;">';
html += '<button id="calc-btn-cg" class="calc-sexe-btn ' + (state.mode === 'cg' ? 'active' : '') + '" onclick="calcSetMode(\'cg\')">Calculer (Cockcroft-Gault)</button>';
html += '<button id="calc-btn-direct" class="calc-sexe-btn ' + (state.mode === 'direct' ? 'active' : '') + '" onclick="calcSetMode(\'direct\')">DFG déjà connu</button>';
html += '</div>';
if (state.mode === 'cg') {
html += '<div style="margin-bottom:12px;display:flex;align-items:center;gap:8px;">';
html += '<span style="font-size:13px;color:#64748b;width:50px">Sexe</span>';
html += '<button id="calc-btn-h" class="calc-sexe-btn ' + (state.sexe === 'H' ? 'active' : '') + '" onclick="calcSetSexe(\'H\')">Homme</button>';
html += '<button id="calc-btn-f" class="calc-sexe-btn ' + (state.sexe === 'F' ? 'active' : '') + '" onclick="calcSetSexe(\'F\')">Femme</button>';
html += '</div>';
html += '<div class="calc-grid">';
html += '<div><label>Âge (ans)</label><input id="calc-age" type="number" placeholder="ex: 65" oninput="calcSetField(\'age\',this.value)"></div>';
html += '<div><label>Poids réel (kg)</label><input id="calc-poids" type="number" placeholder="ex: 70" oninput="calcSetField(\'poids\',this.value)"></div>';
html += '<div><label>Taille (cm)</label><input id="calc-taille" type="number" placeholder="ex: 170" oninput="calcSetField(\'taille\',this.value)"></div>';
html += '<div><label>Créatinine (μmol/L)</label><input id="calc-creat" type="number" placeholder="ex: 120" oninput="calcSetField(\'creat\',this.value)"></div>';
html += '</div>';
} else {
html += '<div style="max-width:250px;">';
html += '<label style="font-size:11px;color:#64748b;font-weight:600;display:block;margin-bottom:4px;">DFG / ClCr (mL/min)</label>';
html += '<input id="calc-dfg-direct" type="number" placeholder="ex: 45" style="width:100%;padding:10px 14px;border-radius:8px;border:1.5px solid #e2e8f0;font-size:18px;font-weight:700;color:#0f2d5e;outline:none;" oninput="calcSetField(\'dfgDirect\',this.value)">';
html += '<div style="font-size:11px;color:#64748b;margin-top:6px;">Valeur fournie par le laboratoire</div>';
html += '</div>';
}
html += '<div id="calc-result-box" class="calc-result-box" style="display:none"></div>';
html += '</div>';
html += '<div class="calc-card">';
html += '<h2>2. Sélection de l\'antimicrobien</h2>';
html += '<input id="calc-search-input" class="calc-search" type="text" placeholder="🔍 Rechercher un antimicrobien..." oninput="calcSetSearch(this.value)">';
html += '<div id="calc-search-results" class="calc-search-results"></div>';
html += '<div id="calc-cats" class="calc-cats"></div>';
html += '<div id="calc-drugs" class="calc-drugs"></div>';
html += '</div>';
html += '<div id="calc-drug-result"></div>';
html += '<div class="calc-warning">⚠️ <b>Avertissement clinique :</b> Cet outil est basé sur le guide APES (RPEI, octobre 2019). Les ajustements servent uniquement à guider la décision clinique — le jugement clinique doit prévaloir. Consulter un pharmacien pour les cas complexes.</div>';
app.innerHTML = html;
renderResult();
renderDrugSelector();
}
window.calcSetMode = function(mode) { state.mode = mode; render(); };
window.calcSetField = function(field, val) {
state[field] = val;
renderResult();
};
window.calcSetSexe = function(sexe) { state.sexe = sexe; renderResult(); };
window.calcSetSearch = function(val) { state.search = val; state.selectedCat = ''; state.selectedDrug = ''; renderDrugSelector(); renderDrugResult(calcClcrFromState().clcr); };
window.calcSetCat = function(cat) { state.selectedCat = cat; state.selectedDrug = ''; renderDrugSelector(); renderDrugResult(calcClcrFromState().clcr); };
window.calcSetDrug = function(drug) { state.selectedDrug = drug; renderDrugSelector(); renderDrugResult(calcClcrFromState().clcr); };
window.calcSelectDrug = function(cat, drug) { state.selectedCat = cat; state.selectedDrug = drug; state.search = ''; var si = document.getElementById('calc-search-input'); if(si) si.value=''; renderDrugSelector(); renderDrugResult(calcClcrFromState().clcr); };
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', render);
} else {
render();
}
}