`;
}
function forgeRenderIcone(el, meta, gameData, childrenMap, ctx, parentFlexDirection) {
// Contrairement au serveur, ne revalide pas _icon_slug contre le
// catalogue curaté (screens/widgets/fontawesome_icons.py) : une valeur
// invalide ne peut être enregistrée que depuis l'éditeur, qui la valide
// déjà à la sauvegarde — un jeu EXPORTÉ n'en produit jamais.
const rawAttrs = el.attributes || {};
const iconSlug = rawAttrs._icon_slug || 'star';
const attrs = forgeVisibleAttrs(el, meta, ctx);
const existingClass = attrs.class || '';
delete attrs.class;
attrs.class = (`${existingClass} icon-svg`).trim();
// data: URI pré-calculée à l'export (voir gameData.icon_data_uris,
// publish/build_scorm_package.py::_build_icon_data_uris) — jamais un
// chemin de fichier, même relatif : Firefox traite chaque ressource
// file:// comme une origine opaque distincte, et mask-image y déclenche
// un fetch en mode CORS quel que soit le chemin ("Blocage d'une requête
// multiorigine... la requête CORS n'utilise pas HTTP", signalé par un
// utilisateur MALGRÉ un chemin relatif par ailleurs correct). Repli sur
// un chemin relatif si jamais l'icône n'a pas été pré-encodée (ne
// devrait pas arriver : _collect_icon_slugs scanne tous les widgets
// "icone", posés directement ou nichés dans un élément de jeu
// réutilisable) — au moins un lien exploitable plutôt qu'un vide.
const iconUrl = gameData.icon_data_uris?.[iconSlug] || (`icons/${iconSlug}.svg`);
const style = `${forgeStyleString(el, parentFlexDirection, el.widget)} --icon-url:url('${iconUrl}');`;
// aria-hidden (RGAA/WCAG 1.1.1) : une icône seule (pas de champ de
// description dédié dans l'éditeur) est traitée comme purement
// décorative — jamais annoncée par un lecteur d'écran, comme le nom du
// slug FontAwesome ("star") n'aurait aucun sens lu à voix haute.
if (!('aria-hidden' in attrs)) attrs['aria-hidden'] = 'true';
return ``;
}
function forgeRenderOnglets(el, meta, gameData, childrenMap, ctx, parentFlexDirection) {
const tabs = childrenMap?.[el.id] || [];
const attrs = forgeVisibleAttrs(el, meta, ctx);
const tabsExtraClasses = attrs.class || '';
delete attrs.class;
const style = `${forgeStyleString(el, parentFlexDirection, el.widget)} display:flex; flex-direction:column;`;
const groupId = forgeAutoId(el);
if (!tabs.length) {
return `
Onglets — ajoute au moins un onglet dans ses réglages.
'
);
}
// Port de render_personnage.py — lit directement gameData.personnage_animations
// (déjà résolu une fois par full_game_payload.py, à l'export) au lieu de
// re-résoudre la bibliothèque de sprites Forge/l'URL personnalisée : ce
// travail est fait UNE fois, à la génération du paquet, jamais à chaque
// rendu côté client.
function forgeRenderPersonnage(el, meta, gameData, childrenMap, ctx, parentFlexDirection) {
const attrs = forgeVisibleAttrs(el, meta, ctx);
attrs['data-personnage'] = '1';
const animations = gameData.personnage_animations?.[String(el.id)];
const frames = animations ? (animations.idle || animations[Object.keys(animations)[0]]) : null;
const idleFrame = frames?.length ? frames[0] : '';
if (idleFrame) attrs.src = idleFrame;
// alt (RGAA/WCAG 1.1.1) : voir render-element.js pour le même défaut
// décoratif sur le widget "image" générique.
if (!('alt' in attrs)) attrs.alt = '';
const style = forgeStyleString(el, parentFlexDirection, el.widget);
return ``;
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
forgeRenderCheckboxOrRadio,
forgeRenderSelect,
forgeRenderFieldset,
forgeRenderTable,
forgeRenderIcone,
forgeRenderOnglets,
forgeRenderPersonnage,
};
const renderMod = require('./render-element.js');
['forgeVisibleAttrs', 'forgeStyleString', 'forgeAttrString', 'forgeApplyCtx', 'forgeHtmlEscape',
'forgeAutoId', 'forgeParentFlexDirection', 'forgeRenderChildren', 'forgeRenderElementHtml'].forEach((name) => {
global[name] = global[name] || renderMod[name];
});
}