diff --git a/templates/play.html b/templates/play.html
index 8b65705f..69369ca4 100644
--- a/templates/play.html
+++ b/templates/play.html
@@ -463,18 +463,49 @@
// tout autre élément cliquable) reste silencieusement inerte pour
// le reste de la partie, sans le moindre message d'erreur visible.
try {
- screenData.elements.filter(function(e){ return !e.parent_id; }).forEach(function(el) {
- if (!el.rendered_html) return;
- // "repeaterItem" (liste de données), "jaugeBar" (2.1 — jauge liée
- // à une donnée) et "visibilityGated" (condition de visibilité —
- // voir render_element_html.py) sont les types d'éléments dont le
- // contenu/l'affichage dépend de la donnée elle-même plutôt que
- // d'une action "Modifier un élément" posée à la main dans le
- // graphe — eux seuls doivent être régénérés après un changement
- // de donnée ou de variable.
- if (el.rendered_html.indexOf('repeaterItem') === -1 && el.rendered_html.indexOf('jaugeBar') === -1 && el.rendered_html.indexOf('visibilityGated') === -1) return;
- const wrapper = screenDiv.querySelector('.playElement[data-el-id="' + el.id + '"] .playElementInner');
- if (wrapper) wrapper.innerHTML = el.rendered_html;
+ // "repeaterItem" (liste de données), "jaugeBar" (2.1 — jauge liée
+ // à une donnée) et "visibilityGated" (condition de visibilité —
+ // voir render_element_html.py) sont les types d'éléments dont le
+ // contenu/l'affichage dépend de la donnée elle-même plutôt que
+ // d'une action "Modifier un élément" posée à la main dans le
+ // graphe — eux seuls doivent être régénérés après un changement
+ // de donnée ou de variable.
+ //
+ // On cible ces éléments à N'IMPORTE QUEL niveau d'imbrication
+ // (pas seulement le premier niveau de l'écran), via leur
+ // data-element-id — disponible sur chaque élément rendu, pas
+ // seulement les éléments de premier niveau. Avant ce correctif,
+ // un Répéteur niché dans un conteneur (comme ici) n'était JAMAIS
+ // du premier niveau, donc c'est son ANCÊTRE de premier niveau
+ // qui portait le marqueur (le Répéteur imbriqué à l'intérieur)
+ // et se retrouvait régénéré en entier à sa place — potentiellement
+ // l'écran entier (jauges, onglets... compris) s'il n'y avait
+ // qu'un seul gros conteneur racine. Comme pour
+ // applyOpenRowBindings(), on ne garde que les éléments marqués
+ // les plus "hauts" dans l'arbre, pour ne jamais régénérer un même
+ // nœud deux fois.
+ const byId = {};
+ screenData.elements.forEach(function(e){ byId[e.id] = e; });
+ function hasMarker(e) {
+ return !!(e.rendered_html && (e.rendered_html.indexOf('repeaterItem') !== -1 || e.rendered_html.indexOf('jaugeBar') !== -1 || e.rendered_html.indexOf('visibilityGated') !== -1));
+ }
+ function hasMarkedAncestor(e) {
+ let p = e.parent_id ? byId[e.parent_id] : null;
+ while (p) {
+ if (hasMarker(p)) return true;
+ p = p.parent_id ? byId[p.parent_id] : null;
+ }
+ return false;
+ }
+ screenData.elements.filter(function(e) {
+ return hasMarker(e) && !hasMarkedAncestor(e);
+ }).forEach(function(el) {
+ const target = screenDiv.querySelector('[data-element-id="' + el.id + '"]');
+ if (!target) return;
+ const container = document.createElement('div');
+ container.innerHTML = el.rendered_html;
+ const newNode = container.firstElementChild;
+ if (newNode) target.replaceWith(newNode);
});
applyOpenRowBindings();
} catch (err) {