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) {