// ---------- Effets visuels par modèle de "❓ Boîte à quiz" ---------- // Registre appelé par static/js/play/dialogue-box-controller.js (JAMAIS // l'inverse) aux points d'extension du moteur PARTAGÉ (forgeShowQuizBox, // forgeQuizBoxAnswer) — voir screens/rendering/quiz_box_templates.py // pour le HTML/CSS de chaque modèle. Ajouter un futur modèle n'oblige // donc jamais à toucher dialogue-box-controller.js : juste une nouvelle // entrée ici (optionnelle) + son CSS/HTML. // Effet PARTAGÉ entre plusieurs modèles "page de quiz" (Manga, Classique, // et tout futur modèle qui voudrait la même mécanique) — demande // explicite : au-dessus de la question, "Question N/M" (nombre de // QUESTIONS de ce dialogue, pas la position brute dans lines qui mélange // aussi les répliques) plutôt que le nom de l'objet déclencheur — et une // barre d'étapes juste en dessous du bandeau. `forgeQuizBoxState`/ // `forgeDialogueBoxState` (déclarés dans dialogue-box-controller.js, // chargé à côté de ce fichier) sont des variables GLOBALES de script // classique (pas de module), lisibles ici telles quelles. // `progressSelector` : chaque modèle a son propre conteneur de barre // d'étapes (data-manga-role/data-classique-role, jamais data-quiz-role — // décoration propre à CHAQUE modèle, pas du contrat partagé). function forgeQuizProgressOnShow(widget, progressSelector) { if (!widget) return; var quizObjectId = widget.getAttribute('data-object-id'); var quizState = (typeof forgeQuizBoxState !== 'undefined') ? forgeQuizBoxState[quizObjectId] : null; if (!quizState) return; var dialogueState = (typeof forgeDialogueBoxState !== 'undefined') ? forgeDialogueBoxState[quizState.dialogueObjectId] : null; if (!dialogueState) return; function isQuestion(line) { return line && line.type === 'question'; } var total = dialogueState.lines.filter(isQuestion).length; if (total === 0) return; var current = dialogueState.lines.slice(0, dialogueState.index + 1).filter(isQuestion).length; var header = widget.querySelector('[data-quiz-role="header"]'); if (header) header.textContent = 'Question ' + current + '/' + total; var progressEl = widget.querySelector(progressSelector); if (progressEl) { var bars = []; for (var i = 1; i <= total; i++) { bars.push(''); } progressEl.innerHTML = bars.join(''); } } function forgeQuizMangaOnShow(widget) { // Défensif : si CE widget est réutilisé pour un quiz déclenché à // nouveau plus tard dans la partie (voir forgeQuizResultQuit, // dialogue-box-controller.js — le widget lui-même n'est jamais // reconstruit, juste masqué), une question fraîchement affichée ne // doit jamais rester sur l'écran de résultat de la fois précédente. if (widget) widget.classList.remove('is-showing-result'); forgeQuizProgressOnShow(widget, '[data-manga-role="progress"]'); } function forgeQuizClassiqueOnShow(widget) { // Même garde-fou que forgeQuizMangaOnShow ci-dessus, ici posée sur // .classiqueContent (pas la racine du widget, voir // render_quiz_box_page_classique) plutôt que sur le widget lui-même. if (widget) { var content = widget.querySelector('.classiqueContent'); if (content) content.classList.remove('is-showing-result'); } forgeQuizProgressOnShow(widget, '[data-classique-role="progress"]'); } // Écrans de résultat de fin de quiz ("classique" ci-dessous, "manga" // plus bas) — appelés par forgeQuizPrepareResultScreen // (dialogue-box-controller.js) UNIQUEMENT à la toute dernière question // (ou si le minuteur du quiz ENTIER expire). `result.correctCount`/ // `result.totalCount` : comptés PAR CE QUIZ (voir forgeQuizBoxState dans // dialogue-box-controller.js), jamais window.forgeQuizScore/ // forgeQuizCorrectAnswered qui cumulent TOUTE la partie. Le seuil de // réussite réutilise EXACTEMENT window.FORGE_SUCCESS_THRESHOLD_PERCENT // (déjà utilisé pour le statut SCORM final, voir // forgeComputeFinalScormStatus) plutôt qu'un réglage à part. Le bouton // QUITTE le quiz (jamais ne le recommence, demande explicite) — voir // forgeQuizResultQuit, dialogue-box-controller.js. function forgeQuizClassiqueOnQuizComplete(widget, result) { if (!widget) return; var content = widget.querySelector('.classiqueContent'); var zone = widget.querySelector('[data-classique-role="result"]'); if (!content || !zone) return; var total = (result && result.totalCount) || 0; var correct = (result && result.correctCount) || 0; var pct = total > 0 ? Math.round((correct / total) * 100) : 0; var threshold = (typeof window !== 'undefined' && typeof window.FORGE_SUCCESS_THRESHOLD_PERCENT === 'number') ? window.FORGE_SUCCESS_THRESHOLD_PERCENT : 70; var passed = pct >= threshold; var titleEl = widget.querySelector('.classiqueHeaderTitle'); var title = titleEl ? titleEl.textContent : 'Quiz'; var quizObjectId = widget.getAttribute('data-object-id'); zone.innerHTML = '
' + correct + ' bonne' + (correct > 1 ? 's' : '') + ' réponse' + (correct > 1 ? 's' : '') + ' sur ' + total + '
' + '« ' + (typeof forgeEscapeHtml === 'function' ? forgeEscapeHtml(title) : title) + ' »
' + 'Score final : ' + correct + ' / ' + total + '
' + '