1(function () { 2 "use strict"; 3 4 /* ---------- données du donut ---------- 5 Libellés, pourcentages et couleurs sont écrits en dur dans le HTML 6 (#wk-usages > .wk-usage : .wk-usage__pct, .wk-usage__dot[style=background], 7 .wk-usage__label). On les lit ici pour dessiner le donut et synchroniser 8 son centre avec la ligne survolée â même ordre que les lignes du HTML. */ 9 /*function dotColor(el) { 10 if (!el) return "#005ea8"; 11 var m = (el.getAttribute("style") || "").match(/background(?:-color)?\s*:\s*([^;]+)/i); 12 return m ? m[1].trim() : (el.style.backgroundColor || "#005ea8"); 13 } 14 var usages = Array.prototype.map.call( 15 document.querySelectorAll("#wk-usages > .wk-usage"), 16 function (row) { 17 var lab = row.querySelector(".wk-usage__label"); 18 var pct = row.querySelector(".wk-usage__pct"); 19 return { 20 label: lab ? lab.textContent.trim() : "", 21 pct: pct ? parseFloat(pct.textContent) || 0 : 0, 22 c: dotColor(row.querySelector(".wk-usage__dot")) 23 }; 24 } 25 );*/ 26 27 /* ---------- donut animé + interactif ---------- */ 28 // Coupe un libellé en 2 lignes équilibrées pour le centre du donut. 29 /*function splitLabel(s) { 30 var w = s.split(" "); 31 if (w.length === 1) return [s, ""]; 32 var best = 1, bestDiff = Infinity; 33 for (var i = 1; i < w.length; i++) { 34 var diff = Math.abs(w.slice(0, i).join(" ").length - w.slice(i).join(" ").length); 35 if (diff < bestDiff) { bestDiff = diff; best = i; } 36 } 37 var l1 = w.slice(0, best).join(" "), l2 = w.slice(best).join(" "); 38 if (l2.charAt(0) === "/") { l1 += " /"; l2 = l2.slice(2); } // pas de ligne qui commence par "/" 39 return [l1, l2]; 40 }*/ 41 42 // p : avancement du balayage (0->1). activeIdx : segment mis en avant (défaut 0). 43 // dim : true => les autres segments passent en opacité .5. Segments et centre 44 // sont dérivés du tableau `usages` (même ordre / mêmes couleurs que les lignes). 45 /*function donut(p, activeIdx, dim) { 46 activeIdx = activeIdx || 0; 47 var R = 90, r = 54, cx = 110, cy = 110, end = -90 + 360 * p, acc = -90, segs = []; 48 function pt(ang, rad) { 49 return [cx + rad * Math.cos(ang * Math.PI / 180), cy + rad * Math.sin(ang * Math.PI / 180)]; 50 } 51 usages.forEach(function (u, i) { 52 var a0 = acc, af = acc + u.pct * 3.6; acc = af; 53 var a1 = Math.min(af, end); if (a1 <= a0 + 0.15) return; 54 var large = (a1 - a0) > 180 ? 1 : 0; 55 var o0 = pt(a0, R), o1 = pt(a1, R), i0 = pt(a1, r), i1 = pt(a0, r); 56 var op = dim && i !== activeIdx ? "0.5" : "1"; 57 segs.push('<path d="M ' + o0[0] + " " + o0[1] + " A " + R + " " + R + " 0 " + large + " 1 " + o1[0] + " " + o1[1] + 58 " L " + i0[0] + " " + i0[1] + " A " + r + " " + r + " 0 " + large + " 0 " + i1[0] + " " + i1[1] + 59 ' Z" fill="' + u.c + '" fill-opacity="' + op + '" style="transition:fill-opacity .18s ease"></path>'); 60 }); 61 var lines = splitLabel(usages[activeIdx].label); 62 return '<svg viewBox="0 0 220 220" style="width:210px;height:210px">' + segs.join("") + 63 '<text x="110" y="100" text-anchor="middle" style="font:800 32px Archivo,sans-serif;fill:#005ea8">' + Math.round(usages[activeIdx].pct * p) + "%</text>" + 64 '<text x="110" y="119" text-anchor="middle" style="font:600 10px Public Sans,sans-serif;fill:#6b7789">' + lines[0] + "</text>" + 65 '<text x="110" y="132" text-anchor="middle" style="font:600 10px Public Sans,sans-serif;fill:#6b7789">' + lines[1] + "</text></svg>"; 66 }*/ 67 68 /* ---------- compteurs de la section "manifeste" ---------- 69 Les valeurs finales sont écrites en dur dans le HTML 70 (#wk-stats .wk-stat__n : "+10 000", "+4 M", "+200 / mois"â¦). 71 On les lit au chargement pour en extraire préfixe / cible / suffixe ; 72 le JS ne fait que rejouer l'effet de comptage 0 -> valeur au scroll. */ 73 function fmt(v) {
74 return Math.round(v).toLocaleString("fr-FR").replace(/[ ]/g, " "); 75 } 76 // "+10 000" -> { prefix: "+", target: 10000, suffix: "" } 77 // "+4 M" -> { prefix: "+", target: 4, suffix: " M" } 78 // "+200 / mois" -> { prefix: "+", target: 200, suffix: " / mois" } 79 function parseStat(txt) { 80 var m = String(txt).match(/^(\D*?)(\d[\d\s.,]*\d|\d)([\s\S]*)$/); 81 if (!m) return { prefix: "", target: 0, suffix: String(txt) }; 82 var n = parseFloat(m[2].replace(/[\s.]/g, "").replace(",", ".")); 83 return { prefix: m[1], target: isNaN(n) ? 0 : n, suffix: m[3] }; 84 } 85 86 /* ---------- hero chips : la puce active suit la question jouée dans le panneau démo ---------- */ 87 var heroChipsEl = document.getElementById("wk-hero-chips"); 88 // Appelé par le panneau démo à chaque nouvelle question : met à jour les 89 // styles des puces déjà présentes dans le HTML (aucune reconstruction). 90 function setActiveChip(i) { 91 var chips = heroChipsEl ? heroChipsEl.children : []; 92 if (!chips.length) return; 93 i = i % chips.length; 94 for (var k = 0; k < chips.length; k++) { 95 chips[k].classList.toggle("is-active", k === i); 96 } 97 } 98 99 /* ---------- compteurs + donut : animation au scroll (ex-componentDidMount) ---------- */ 100 function easeOutCubic(p) { return 1 - Math.pow(1 - p, 3); } 101 // Stepper basé sur setInterval : fiable même quand requestAnimationFrame est gelé 102 // (onglet en arrière-plan, file://, throttling navigateur). Garantit toujours cb(1). 103 function animate(dur, cb) { 104 var start = Date.now(); 105 cb(0); 106 var id = setInterval(function () { 107 var p = Math.min(1, (Date.now() - start) / dur); 108 cb(easeOutCubic(p)); 109 if (p >= 1) clearInterval(id); 110 }, 16); 111 } 112 113 var statsEl = document.getElementById("wk-stats"); 114 var donutEl = document.getElementById("wk-donut"); 115 116 // Cibles lues UNE fois dans le HTML, avant toute animation. 117 var statNums = statsEl ? statsEl.querySelectorAll(".wk-stat__n") : []; 118 var statSpecs = []; 119 for (var s = 0; s < statNums.length; s++) { 120 statSpecs.push(parseStat(statNums[s].textContent.trim())); 121 } 122 123 // Ne touche qu'au chiffre de chaque carte (déjà présente dans le HTML), 124 // pas au libellé qui reste celui écrit en dur. 125 function paintStats(sp) { 126 for (var i = 0; i < statNums.length && i < statSpecs.length; i++) { 127 var st = statSpecs[i]; 128 statNums[i].textContent = st.prefix + fmt(st.target * sp) + st.suffix; 129 } 130 } 131 /*function paintDonut(p) { donutEl.innerHTML = donut(p, 0, false); }*/ 132 133 // Met à jour le donut EN PLACE au survol d'une ligne (opacités + centre), 134 // sans reconstruire le SVG -> la transition d'opacité peut jouer. 135 /*function setDonutActive(idx, dim) { 136 var paths = donutEl.querySelectorAll("path"); 137 for (var i = 0; i < paths.length; i++) { 138 paths[i].setAttribute("fill-opacity", dim && i !== idx ? "0.2" : "1"); 139 } 140 var texts = donutEl.querySelectorAll("text"); 141 var lines = splitLabel(usages[idx].label); 142 if (texts[0]) texts[0].textContent = usages[idx].pct + "%"; 143 if (texts[1]) texts[1].textContent = lines[0]; 144 if (texts[2]) texts[2].textContent = lines[1] || ""; 145 }*/ 146 147 // Le donut est un graphique généré, sans équivalent statique : il démarre 148 // à 0 puis se dessine au scroll. Les compteurs, eux, gardent leur valeur 149 // finale (écrite en dur) jusqu'à ce que l'animation de comptage démarre. 150 /*paintDonut(0);*/ 151 152 var statsStarted = false, donutStarted = false, donutReady = false; 153 function runStats() { if (statsStarted) return; statsStarted = true; animate(1400, paintStats); } 154 /*function runDonut() { 155 if (donutStarted) return; donutStarted = true; 156 animate(1200, function (p) { 157 paintDonut(p); 158 if (p >= 1) donutReady = true; // balayage terminé : le survol peut agir 159 }); 160 }*/ 161 162 // Déclenche cb() la première fois que le bloc entre dans le viewport. 163 // On sonde la position sur un petit timer (getBoundingClientRect) : ça ne dépend 164 // ni des événements scroll ni d'IntersectionObserver, donc fiable partout. 165 function onEnterView(el, cb) { 166 if (!el) return; 167 function inView() { 168 var r = el.getBoundingClientRect(); 169 var vh = window.innerHeight || document.documentElement.clientHeight; 170 // le haut du bloc est entré d'au moins 60px et le bloc n'est pas déjà remont
170é au-dessus 171 return r.top < vh - 60 && r.bottom > 0; 172 } 173 if (inView()) { cb(); return; } 174 var poll = setInterval(function () { 175 if (inView()) { clearInterval(poll); cb(); } 176 }, 200); 177 } 178 onEnterView(statsEl, runStats); 179 /*onEnterView(donutEl, runDonut);*/ 180 181 /* ---------- survol des lignes d'usage <-> donut ---------- */ 182 (function () { 183 var rows = document.querySelectorAll("#wk-usages > a"); 184 for (var i = 0; i < rows.length; i++) { 185 (function (idx) { 186 rows[idx].addEventListener("mouseenter", function () { 187 if (donutReady) setDonutActive(idx, true); // segment idx opaque, autres à .5 188 }); 189 rows[idx].addEventListener("mouseleave", function () { 190 if (donutReady) setDonutActive(0, false); // retour à l'état par défaut 191 }); 192 })(i); 193 } 194 })(); 195 196 /* ---------- démo : assistant interactif animé (ex-playDemo du fichier B) ---------- */ 197 (function () { 198 var demoScroll = document.getElementById("wk-demo-scroll"); 199 var demoInput = document.getElementById("wk-demo-input"); 200 if (!demoScroll || !demoInput) return; 201 202 function esc(s) { 203 return String(s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); 204 } 205 206 var scenarios = [ 207 { 208 q: "Le DGS peut-il exercer les fonctions d'officier d'état civil ?", 209 a: "Oui, mais uniquement par délégation du maire, et sous conditions strictes.\n\n" + 210 "Le DGS doit être fonctionnaire titulaire de la commune : l'article R. 2122-10 du CGCT réserve la délégation d'officier d'état civil aux seuls fonctionnaires titulaires. La délégation doit faire l'objet d'un arrêté nominatif du maire, être publiée et transmise au contrôle de légalité pour devenir exécutoire.\n\n" + 211 "Attention au détachement : un fonctionnaire détaché dans la commune ne peut pas recevoir cette délégation â les magistrats du parquet estiment qu'il n'est pas « fonctionnaire titulaire de la commune ». En revanche, aucune disposition ne limite la fonction à la filière administrative.\n\n" + 212 "Points de vigilance : la délégation devient caduque dès que le maire ou le DGS cesse ses fonctions ; le maire conserve toujours la possibilité d'exercer personnellement ses fonctions d'officier d'état civil et reste responsable du contrôle des fonctions déléguées.", 213 srcs: [ 214 { t: "Délégation de fonctions et délégation de signature", ref: "Fiche WEKA n°5900", maj: "mai 2026", ex: "« La délégation doit faire l'objet d'un arrêté publié (CE, 12 mars 1975, Commune de Loges-Margueron). Pour être exécutoire, elle doit être transmise au contrôle de légalité et notifiée au délégataire. »" }, 215 { t: "La qualité d'officier de l'état civil", ref: "Fiche WEKA n°1240", maj: "avril 2026", ex: "« Seuls les fonctionnaires titulaires de la commune peuvent recevoir délégation dans les fonctions d'officier d'état civil, à l'exclusion des agents recrutés par voie de détachement. »" }, 216 { t: "Code général des collectivités territoriales", ref: "Art. R. 2122-10", maj: "en vigueur", ex: "« Le maire peut déléguer à un ou plusieurs fonctionnaires titulaires de la commune les fonctions qu'il exerce en tant qu'officier de l'état civil. »" } 217 ] 218 }, 219 { 220 q: "Rédige un arrêté interdisant la circulation rues Paul Doumer, Jeanne d'Arc et George Sand du 10 au 14 avril pour la brocante annuelle", 221 a: "ARRÃTà MUNICIPAL N° [à compléter]\nPortant interdiction temporaire de circulation et de stationnement â Brocante annuelle\n\n" + 222 "VU le Code général des collectivités territoriales, notamment l'article L. 2213-2 relatif aux pouvoirs de police du maire en matière de circulation et de stationnement ;\n" + 223 "VU le Code de la route, notamment les articles R. 417-6 et R. 411-25 ;\n" + 224 "VU la demande de l'association « Saint-Dié en folie » sollicitant l'organisation de sa brocante annuelle ;\n" + 225 "CONSIDÃRANT la nécessité d'assurer la sécurité des participants et des visiteurs ;\n\n" + 226 "ARRÃTE\n\n" + 227 "Article 1 â Du jeudi 10 avril à 8h00 au lundi 14 avril à 22h00, la circulation de tous véhicules et le stationnement sont interdits rue Paul Doumer, rue Jeanne d'Arc et rue George Sand, sur toute leur longueur.\n\n" + 228 "Article 2 â Sont autorisés à circuler : les véhicules de secours et de police ; les véhicules des exposants pour le montage (10 avril, 6h-8h) et le démontage ; les riverains sur justificatif.\n\n" + 229 "Article 3 â La signalisation réglementaire sera mise en place par les services techniques. Les panneaux seront installés 48 heures avant le début de l'interdiction.", 230 srcs: [ 231 { t: "Comment gérer le commerce ambulant sur le territoire communal ?", ref: "Fiche WEKA n°7417", maj: "juin 2026", ex: "« Le maire tient de l'article L. 2213-2 du CGCT le pouvoir d'interdire à certaines heures l'accès de certaines voies, dans l'intérêt de la commodité et de la sûreté de la circulation. »" }, 232 { t: "Police municipale : règles relatives à l'arrêt et au stationnement", ref: "Fiche WEKA n°1773", maj: "mai 2026", ex: "« L'arrêté doit être affiché sur place 48 heures avant le début de l'interdiction et transmis au contrôle de légalité avant sa mise en application. »" }, 233 { t: "Code de la route", ref: "Art. R. 417-6 et R. 411-25", maj: "en vigueur", ex: "« Tout véhicule en stationnement gênant peut faire l'objet d'une mise en fourrière aux frais de son propriétaire. »" } 234 ] 235 }, 236 { 237 q: "Calcule une indemnité de rupture conventionnelle : 6 ans d'ancienneté, 22 034,40 ⬠brut, adjoint technique territorial", 238 a: "Rémunération mensuelle brute de référence : 22 034,40 ⬠÷ 12 = 1 836,20 â¬.\n\n" + 239 "Montant plancher â avec 6 ans d'ancienneté, vous êtes dans la première tranche du barème (1/4 de mois par année) : (1 836,20 ⬠÷ 4) à 6 = 2 754,30 ⬠brut.\n\n" + 240 "Montant plafond â il ne peut excéder 1/12 de la rémunération brute annuelle par année d'ancienneté : 1 836,20 ⬠à 6 = 11 017,20 ⬠brut.\n\n" + 241 "Votre marge de négociation se situe donc entre 2 754,30 ⬠et 11 017,20 â¬.\n\n" + 242 "Points importants : seules les années de services dans la fonction publique territoriale sont retenues ; le calcul se base sur la rémunération brute de l'année civile précédant la rupture ; certaines primes sont exclues (remboursements de frais, indemnités de jury ou d'enseignement, prime de mobilité géographique) ; l'indemnité bénéficie d'une exonération partielle de CSG/CRDS et d'impôt sur le revenu.", 243 srcs: [ 244 { t: "L'indemnité spécifique de rupture conventionnelle (ISRC)", ref: "Fiche WEKA n°13585", maj: "juin 2026", ex: "« Le montant de l'indemnité ne peut être inférieur à un quart de mois de rémunération brute par année d'ancienneté pour les dix premières années. »" }, 245 { t: "Décret n°2019-1593 du 31 décembre 2019", ref: "Texte consolidé", maj: "en vigueur", ex: "« Le montant maximum de l'indemnité ne peut excéder un douzième de la rémunération brute annuelle de référence par année d'ancienneté, dans la limite de vingt-quatre ans. »" }, 246 { t: "La rupture conventionnelle dans la fonction publique territoriale", ref: "Fiche WEKA n°13346", maj: "mai 2026", ex: "« Les deux parties négocient librement le montant de l'indemnité dans les bornes fixées par le décret, à l'issue d'au moins un entretien préalable. »" } 247 ] 248 }, 249 { 250 q: "Complète cette clause de marché public sur les engagements écologiques", 251 a: "Votre clause constitue une bonne base ; voici des compléments pour la rendre opérationnelle.\n\n" + 252 "Consommation énergétique â « Les parties s'engagent à optimiser leur consommation énergétique en privilégiant les matériels à faible consommation électrique pour les équipements de projection, audiovisuel et informatique, et veillent à une gestion raisonnée de l'éclairage, de la sonorisation et de la consommation d'eau potable. »\n\n" + 253 "Gestion des déchets â « Les parties réduisent la production de déchets à la source en supprimant autant que possible les contenants et ustensiles jetables au profit de solutions réutilisables, mettent en place un tri sélectif avec signalétique adaptée et assurent une valorisation matière ou énergétique des déchets produits. »\n\n" + 254 "Mobilités â « Les parties privilégient les mobilités douces, encouragent le covoiturage pour l'accès à l'événement et retiennent, dans la mesure du possible, des intervenants locaux afin de limiter les déplacements. »\n\n" + 255 "Suivi â « Les parties mettent en place des indicateurs de suivi des actions déployées et établissent un bilan environnemental à l'issue de l'événement, dans une logique d'amélioration continue. »", 256 srcs: [ 257 { t: "Intégrer des enjeux de développement durable dans un marché public d'organisation d'événements", ref: "Fiche WEKA n°2174", maj: "juin 2026", ex: "« Les clauses environnementales doivent être formulées de manière mesurable et vérifiable pour être opposables au titulaire du marché. »" }, 258 { t: "Organiser un événement écoresponsable", ref: "Fiche WEKA n°7104", maj: "mai 2026", ex: "« La suppression des contenants et ustensiles jetables, au profit de solutions réutilisables, constitue le premier levier de réduction des déchets à la source. »" }, 259 { t: "Code de l'environnement", ref: "Art. L. 541-15-10", maj: "en vigueur", ex: "« Sont fixées des obligations en matière de réduction des déchets et de promotion de l'économie circulaire, notamment pour la restauration collective. »" } 260 ] 261 } 262 ]; 263 264 var st = { typedQ: "", demoQ: "", demoA: "", qShown: false, thinking: false, aShown: false, chipsShown: false, srcShown: false, t
264yping: false, src: -1, curSrcs: null }; 265 var timers = []; 266 267 function sd() { demoScroll.scrollTop = demoScroll.scrollHeight; } 268 269 function render() { 270 var parts = []; 271 if (st.qShown) { 272 parts.push('<div style="display:flex;justify-content:flex-end"><span style="background:#EEF2F6;color:#103A66;font:500 14px/1.4 \'Public Sans\',sans-serif;padding:10px 14px;border-radius:12px 12px 4px 12px">' + esc(st.demoQ) + "</span></div>"); 273 } 274 if (st.thinking) { 275 parts.push('<div style="display:flex;gap:5px;padding:4px 2px">' + 276 '<span style="width:7px;height:7px;border-radius:50%;background:#005ea8;animation:wkblink 1.1s infinite"></span>' + 277 '<span style="width:7px;height:7px;border-radius:50%;background:#005ea8;animation:wkblink 1.1s .18s infinite"></span>' + 278 '<span style="width:7px;height:7px;border-radius:50%;background:#005ea8;animation:wkblink 1.1s .36s infinite"></span></div>'); 279 } 280 if (st.aShown) { 281 var chips = st.chipsShown ? '<span style="display:inline-flex;gap:4px;vertical-align:middle;margin-left:5px">' + 282 '<span style="background:#005ea8;color:#fff;font:700 9px/1;padding:3px 5px;border-radius:4px;cursor:pointer">1</span>' + 283 '<span style="background:#005ea8;color:#fff;font:700 9px/1;padding:3px 5px;border-radius:4px;cursor:pointer">2</span>' + 284 '<span style="background:#005ea8;color:#fff;font:700 9px/1;padding:3px 5px;border-radius:4px;cursor:pointer">3</span></span>' : ""; 285 parts.push('<p style="margin:0;font:400 13.5px/1.65 \'Public Sans\',sans-serif;color:#45525F;white-space:pre-wrap">' + esc(st.demoA) + chips + "</p>"); 286 } 287 if (st.srcShown) { 288 var rows = (st.curSrcs || []).map(function (s, idx) { 289 return '<div data-src="' + idx + '" style="display:flex;gap:8px;cursor:pointer;align-items:baseline">' + 290 '<span style="color:#eb7a61;font-weight:800">âº</span>' + 291 '<span class="wk-demo-src" style="font:400 12px/1.4 \'Public Sans\',sans-serif;color:' + (st.src === idx ? "#005ea8" : "#45525F") + ";text-decoration:" + (st.src === idx ? "underline" : "none") + '">' + esc(s.t) + "</span></div>"; 292 }).join(""); 293 var active = ""; 294 if (st.src >= 0 && st.curSrcs && st.curSrcs[st.src]) { 295 var a = st.curSrcs[st.src]; 296 active = '<div style="margin-top:11px;background:#fff;border:1px solid #c4d9ec;border-radius:9px;padding:11px 13px">' + 297 '<div style="display:flex;justify-content:space-between;align-items:center;gap:10px;margin-bottom:7px">' + 298 '<span style="font:700 11.5px/1.3 Archivo,sans-serif;color:#103A66">' + esc(a.ref) + "</span>" + 299 '<span style="flex:none;font:700 10px/1 \'Public Sans\',sans-serif;color:#0a929f;background:#E4F3F4;padding:3px 7px;border-radius:5px">' + esc(a.maj) + "</span></div>" + 300 '<p style="margin:0 0 9px;font:400 11.5px/1.5 \'Public Sans\',sans-serif;color:#5a6675">' + esc(a.ex) + "</p>" + 301 '<span style="font:700 11.5px/1 \'Public Sans\',sans-serif;color:#005ea8;cursor:pointer">Ouvrir la fiche complète â</span></div>'; 302 } 303 parts.push('<div style="background:#F3F8FD;border:1px solid #d3e4f5;border-radius:12px;padding:14px">' + 304 '<div style="font:700 12px/1 Archivo,sans-serif;color:#103A66;margin-bottom:10px">Sources · fiches WEKA & textes officiels</div>' + 305 '<div style="display:flex;flex-direction:column;gap:9px">' + rows + "</div>" + active + "</div>"); 306 } 307 demoScroll.innerHTML = parts.join(""); 308 309 var caret = st.typing ? '<span style="display:inline-block;width:1.5px;height:13px;background:#005ea8;margin-left:2px;vertical-align:-2px;animation:wkcaret 1s infinite"></span>' : ""; 310 var placeholder = (!st.typing && !st.typedQ) ? '<span style="color:#9aa3b0">Posez votre question â¦</span>' : ""; 311 demoInput.innerHTML = esc(st.typedQ) + caret + placeholder; 312 } 313 314 // clic sur une source -> ouvre / referme son extrait 315 demoScroll.addEventListener("click", function (e) { 316 var row = e.target.closest ? e.target.closest("[data-src]") : null; 317 if (!row) return; 318 var idx = parseInt(row.getAttribute("data-src"), 10); 319 st.src = st.src === idx ? -1 : idx; 320 render(); sd(); 321 }); 322 323 function play(i) { 324 var sc = scenarios[i % scenarios.length];
325 timers.forEach(clearTimeout); timers = []; 326 function T(fn, ms) { timers.push(setTimeout(fn, ms)); } 327 328 st = { typedQ: "", demoQ: "", demoA: "", qShown: false, thinking: false, aShown: false, chipsShown: false, srcShown: false, typing: true, src: -1, curSrcs: sc.srcs }; 329 setActiveChip(i); // la puce du hero correspondant à cette question s'active 330 render(); 331 demoScroll.scrollTop = 0; 332 333 var t = 700; 334 for (var k = 1; k <= sc.q.length; k++) { 335 (function (frag) { T(function () { st.typedQ = frag; render(); }, t); })(sc.q.slice(0, k)); 336 t += 36; 337 } 338 t += 450; 339 T(function () { st.typedQ = ""; st.typing = false; st.demoQ = sc.q; st.qShown = true; st.thinking = true; render(); sd(); }, t); 340 t += 1100; 341 T(function () { st.thinking = false; st.aShown = true; render(); sd(); }, t); 342 for (var m = 1; m <= sc.a.length; m += 9) { 343 (function (frag) { T(function () { st.demoA = frag; render(); sd(); }, t); })(sc.a.slice(0, m)); 344 t += 22; 345 } 346 T(function () { st.demoA = sc.a; st.chipsShown = true; render(); sd(); }, t); t += 400; 347 T(function () { st.srcShown = true; render(); sd(); }, t); t += 1300; 348 T(function () { st.src = 0; render(); sd(); }, t); t += 6200; 349 T(function () { play(i + 1); }, t); 350 } 351 352 render(); 353 play(0); 354 })(); 355})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.