Enrichit le reporting SCORM/xAPI et met en conformité RGAA le player
SCORM/xAPI : - Ajoute l'export SCORM 2004 (3rd/4th edition) au choix, en plus du 1.2 par défaut : sépare completion_status/success_status (un échec reste "completed" au lieu de retomber à tort en "incomplete" comme force la 1.2), remonte aussi cmi.interactions.n.* par question répondue. - Fournit cmi.core.score.min/max (1.2 et 2004), calculé depuis les récompenses de quiz du jeu, pour que le LMS affiche un vrai pourcentage au lieu du score brut à tort étiqueté "%". - Libellés de verbes xAPI en français en plus de l'anglais. Accessibilité (RGAA/WCAG 2.1 AA) sur le player : - Navigation clavier des objets de scène "au clic"/"au survol" (tabindex, role, Entrée/Espace, focus/blur). - alt sur les images (nom auteur ou décoratif), aria-hidden sur les icônes seules, role="dialog"/aria-live sur les boîtes de dialogue/quiz. - Landmark <main> + titre de page, respect de prefers-reduced-motion. - Le quiz n'avance plus automatiquement après un délai fixe : bouton "Continuer →" explicite (RGAA 2.2.1). - Avertissement de contraste dans l'éditeur de style de dialogue. - Déclaration d'accessibilité téléchargeable depuis la modale d'export.
This commit is contained in:
@@ -606,6 +606,51 @@ function refreshSceneObjectNames() {
|
||||
.then(function (json) { SCENE_OBJECT_NAMES = json; });
|
||||
}
|
||||
|
||||
// RGAA/WCAG 1.4.3 (contraste minimum) : les couleurs du "🎨 Style"
|
||||
// ci-dessous sont librement choisies par l'auteur, sans aucune validation
|
||||
// serveur (voir sanitize_dialogue_box_style.py, qui n'accepte qu'une
|
||||
// chaîne de longueur bornée, jamais un ratio) — juste un AVERTISSEMENT
|
||||
// visuel dans l'éditeur, jamais un blocage (le créateur reste libre de
|
||||
// son choix esthétique, ex. un jeu volontairement sombre/stylisé).
|
||||
function forgeHexToRgb(hex) {
|
||||
hex = String(hex || '').replace('#', '');
|
||||
if (hex.length === 3) hex = hex.split('').map(function (c) { return c + c; }).join('');
|
||||
var num = parseInt(hex, 16);
|
||||
if (isNaN(num) || hex.length !== 6) return [0, 0, 0];
|
||||
return [(num >> 16) & 255, (num >> 8) & 255, num & 255];
|
||||
}
|
||||
|
||||
function forgeRelativeLuminance(rgb) {
|
||||
var srgb = rgb.map(function (c) {
|
||||
c = c / 255;
|
||||
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
||||
});
|
||||
return 0.2126 * srgb[0] + 0.7152 * srgb[1] + 0.0722 * srgb[2];
|
||||
}
|
||||
|
||||
// Formule de contraste WCAG standard (relative luminance) — 4.5:1 est le
|
||||
// seuil "AA" pour du texte normal (RGAA 3.2/critère 1.4.3).
|
||||
function forgeContrastRatio(hex1, hex2) {
|
||||
var l1 = forgeRelativeLuminance(forgeHexToRgb(hex1));
|
||||
var l2 = forgeRelativeLuminance(forgeHexToRgb(hex2));
|
||||
var lighter = Math.max(l1, l2), darker = Math.min(l1, l2);
|
||||
return (lighter + 0.05) / (darker + 0.05);
|
||||
}
|
||||
|
||||
function checkDialogueBoxContrast(style) {
|
||||
var warningEl = document.getElementById('dboxContrastWarning');
|
||||
if (!warningEl) return;
|
||||
var ratios = [
|
||||
forgeContrastRatio(style.text_color, style.header_bg),
|
||||
forgeContrastRatio(style.text_color, style.body_bg),
|
||||
forgeContrastRatio(style.text_color, style.footer_bg),
|
||||
];
|
||||
var minRatio = Math.min.apply(null, ratios);
|
||||
warningEl.textContent = minRatio < 4.5
|
||||
? '⚠️ Contraste insuffisant (' + minRatio.toFixed(1) + ':1, minimum recommandé 4.5:1) — le texte risque d\'être difficile à lire.'
|
||||
: '';
|
||||
}
|
||||
|
||||
// "🎨 Style" du widget "💬 Boîte de dialogue" (voir screens/rendering/
|
||||
// dialogue_box_style.py) — envoie TOUJOURS les 7 champs ensemble (même
|
||||
// convention "replace-all" que saveSceneObjectCollision), jamais un seul
|
||||
@@ -620,6 +665,7 @@ function saveDialogueBoxStyle(objectId) {
|
||||
body_bg: document.getElementById('dbox-body-bg').value,
|
||||
footer_bg: document.getElementById('dbox-footer-bg').value,
|
||||
};
|
||||
checkDialogueBoxContrast(style);
|
||||
var el = document.getElementById('el-' + objectId);
|
||||
var widget = el && el.querySelector('.dialogueBoxWidget');
|
||||
if (widget) {
|
||||
|
||||
Reference in New Issue
Block a user