// Export Web/SCORM — port de screens/rendering/render_jauge.py (jauge liée
// à une donnée : remplissage % + dégradé de couleur recalculés depuis un
// champ numérique d'un objet). Lit gameData.data/fields_meta directement
// au lieu d'une requête SQL (voir _read_field_value côté serveur).
function forgeHexToRgb(hexColor) {
let hex = (hexColor || '#5b8cff').replace(/^#/, '');
if (hex.length !== 6) hex = '5b8cff';
return [
Number.parseInt(hex.substr(0, 2), 16),
Number.parseInt(hex.substr(2, 2), 16),
Number.parseInt(hex.substr(4, 2), 16),
];
}
// Extrait de forgeInterpolateColor (lot 4 "modernisation JS", S7721) :
// fonction pure (aucune fermeture sur lo/hi/mixed/ratio), pas besoin
// d'être redéfinie à chaque appel.
function forgeByteToHex(n) {
return n.toString(16).padStart(2, '0');
}
function forgeInterpolateColor(lowHex, highHex, ratio) {
const lo = forgeHexToRgb(lowHex); const
hi = forgeHexToRgb(highHex);
const mixed = lo.map((c, i) => Math.round(c + (hi[i] - c) * ratio));
return `#${mixed.map(forgeByteToHex).join('')}`;
}
// Port de _read_field_value — repli sur la ligne la PLUS RÉCENTE si
// row_id est absent/introuvable (comportement historique, un objet à une
// seule ligne n'est jamais impacté).
function forgeReadFieldValue(gameData, definitionId, rowId, fieldName) {
if (!definitionId || !fieldName) return null;
definitionId = String(definitionId);
const rows = gameData.data?.[definitionId];
if (!rows?.length) return null;
let row = null;
if (rowId) row = rows.find((r) => String(r.id) === String(rowId));
if (!row) row = rows[0];
return row[fieldName];
}
// Label superposé qui affiche la valeur BRUTE du champ (indépendant du
// pourcentage/couleur) — extrait de forgeRenderJauge (lot 4 "modernisation
// JS", S3776).
function forgeJaugeValueLabelHtml(afficherValeur, rawValue) {
if (!afficherValeur) return '';
return '
${
forgeHtmlEscape(rawValue !== null && rawValue !== undefined ? String(rawValue) : '—')}
`;
}
// Label du NOM (ex. "Alice"), lu depuis un second champ optionnel — extrait
// de forgeRenderJauge.
function forgeJaugeNameLabelHtml(gameData, definitionId, rowId, fieldNameNom, nomPosition, nomAlignement, nomStyle) {
if (!fieldNameNom) return '';
const nomValue = forgeReadFieldValue(gameData, definitionId, rowId, fieldNameNom);
if (nomValue === null || nomValue === undefined || nomValue === '') return '';
const textAlign = (nomPosition === 'dessus' && nomAlignement === 'gauche') ? 'left' : 'center';
const {
nomPolice, nomTaille, nomGras, nomItalique,
} = nomStyle;
return `${forgeHtmlEscape(String(nomValue))}
`;
}
function forgeRenderJauge(el, meta, gameData, childrenMap, ctx, parentFlexDirection) {
const attrs = el.attributes || {};
const nomPosition = attrs._nom_position || 'dessus';
const nomAlignement = attrs._nom_alignement || 'centre';
const nomPolice = attrs._nom_police || 'inherit';
const nomTaille = attrs._nom_taille || '12';
const nomGras = attrs._nom_gras === '1';
const nomItalique = attrs._nom_italique === '1';
const flexDir = nomPosition === 'cote' ? 'row' : 'column';
const alignItems = nomPosition === 'cote' ? 'center' : 'stretch';
const style = `${forgeStyleString(el, parentFlexDirection, el.widget)} display:flex; flex-direction:${flexDir}; align-items:${alignItems}; justify-content:center; gap:4px;`;
const definitionId = attrs._definition_id;
const rowId = attrs._row_id;
const fieldName = (attrs._champ || '').trim();
const fieldNameNom = (attrs._champ_nom || '').trim();
let minV = Number.parseFloat(attrs._min); if (Number.isNaN(minV)) minV = 0;
let maxV = Number.parseFloat(attrs._max); if (Number.isNaN(maxV)) maxV = 100;
const couleurBasse = attrs._couleur_basse || '#e05252';
const couleurHaute = attrs._couleur_haute || '#4caf50';
const afficherValeur = attrs._afficher_valeur === '1';
const rawValue = fieldName ? forgeReadFieldValue(gameData, definitionId, rowId, fieldName) : null;
let value = Number.parseFloat(rawValue);
if (Number.isNaN(value)) value = minV;
const span = (maxV - minV) || 1;
const ratio = Math.max(0, Math.min(1, (value - minV) / span));
const percent = Math.round(ratio * 1000) / 10;
const fillColor = forgeInterpolateColor(couleurBasse, couleurHaute, ratio);
const labelHtml = forgeJaugeValueLabelHtml(afficherValeur, rawValue);
const nomStyle = {
nomPolice, nomTaille, nomGras, nomItalique,
};
const nameHtml = forgeJaugeNameLabelHtml(
gameData,
definitionId,
rowId,
fieldNameNom,
nomPosition,
nomAlignement,
nomStyle,
);
const extraClasses = Object.keys(attrs)
.filter((k) => k.startsWith('_class_') && attrs[k])
.map((k) => attrs[k]).join(' ');
const barClass = forgeHtmlEscape((`progress jaugeBar ${extraClasses}`).trim());
const bar = ``;
const barWrap = '${bar}${labelHtml}
`;
return `${
nameHtml}${barWrap}
`;
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
forgeRenderJauge, forgeHexToRgb, forgeInterpolateColor, forgeReadFieldValue,
};
const renderMod = require('./render-element.js');
global.forgeStyleString = global.forgeStyleString || renderMod.forgeStyleString;
global.forgeHtmlEscape = global.forgeHtmlEscape || renderMod.forgeHtmlEscape;
global.forgeAutoId = global.forgeAutoId || renderMod.forgeAutoId;
}