Basculer le menu
Changer de menu des préférences
Basculer le menu personnel
Non connecté(e)
Votre adresse IP sera visible au public si vous faites des modifications.

« Module:Infobox/Core » : différence entre les versions

De Nefald
Hiob (discussion | contributions)
Aucun résumé des modifications
Balise : Révoqué
Hiob (discussion | contributions)
Port depuis codex.nefald.fr : masquage des sections vides, champs sans label pleine largeur, icône configurable par fonction (via update-page on MediaWiki MCP Server)
 
(6 versions intermédiaires par le même utilisateur non affichées)
Ligne 2 : Ligne 2 :
local getArgs = require('Module:Arguments').getArgs
local getArgs = require('Module:Arguments').getArgs


-- Fonction pour créer l'HTML de l'infobox
-- Fonction pour générer le lien de carte dynamique
function p.createInfobox(config, args)
local function buildMapLink(args)
    local root = mw.html.create('div')
    -- Vérifier les champs obligatoires
         :addClass('infobox')
    if not args.x or args.x == '' then return nil end
        :attr('role', 'region')
    if not args.y or args.y == '' then return nil end
         :attr('aria-label', 'Infobox')
    if not args.z or args.z == '' then return nil end
    if not args.worldname or args.worldname == '' then return nil end
   
    -- Valeurs par défaut pour les champs optionnels
    local mapname = args.mapname or 'flat'
    local zoom = args.zoom or '4'
   
    -- Construction de l'URL
    local url = string.format(
        'https://map.nefald.fr/?worldname=%s&mapname=%s&zoom=%s&x=%s&y=%s&z=%s',
        mw.uri.encode(args.worldname, 'PATH'),
        mw.uri.encode(mapname, 'PATH'),
        zoom,
        args.x,
        args.y,
         args.z
    )
   
    -- Retourner le lien formaté
    return string.format('[%s %s, %s, %s]', url, args.x, args.y, args.z)
end
 
-- Fonction de validation du champ "type"
local function validateTypeField(args, config)
    local typeValue = args.type or args['type']
   
    -- Si pas de configuration pour le type, pas de validation
    if not config.typeField then
        return { valid = true }
    end
   
    -- Si le champ est requis et vide
    if config.typeField.required and (not typeValue or typeValue == '') then
         return {
            error = 'Le champ "type" est obligatoire.',
            valid = false
        }
    end
   
    -- Si pas de valeur, mais pas requis, c'est valide
    if not typeValue or typeValue == '' then
        return { valid = true }
    end
   
    -- Nettoyer la valeur
    local cleanType = mw.text.trim(mw.ustring.lower(typeValue))
   
    -- Vérifier si la valeur est dans la liste autorisée
    if config.typeField.allowedValues and not config.typeField.allowedValues[cleanType] then
        local errorMsg = config.typeField.errorMessage or 'Valeur de type non autorisée : ' .. typeValue
        return {
            error = errorMsg,
            valid = false
        }
    end
   
    return {
        valid = true,
        cleanValue = cleanType,
        displayValue = config.typeField.allowedValues and config.typeField.allowedValues[cleanType] and config.typeField.allowedValues[cleanType].display or typeValue
    }
end


     -- En-tête avec titre
-- Fonction pour gérer la catégorisation automatique
     local header = root:tag('div'):addClass('infobox-header')
local function addCategories(args, config, frame)
     -- Vérifier qu'on est dans le namespace principal (0) ou namespace plugin (3000)
     local title = mw.title.getCurrentTitle()
    if title.namespace ~= 0 and title.namespace ~= 3000 then
        return ''
    end
      
      
    -- Conteneur pour l'icône et le titre
     local categoryString = ''
     local headerContent = header:tag('div'):addClass('infobox-header-content')
      
      
     -- Gestion de l'icône
     -- Nouvelles catégories configurables (fonction categories)
    if config.icone then
    if type(config.categories) == 'function' then
        local iconSpan = headerContent:tag('span'):addClass('infobox-icon')
        local cats = config.categories(args, config, frame)
       
        if cats then
        if type(config.icone) == 'table' then
             for _, cat in ipairs(cats) do
            -- Icône FontAwesome
                categoryString = categoryString .. '[[Catégorie:' .. cat .. ']]'
            local iconType = config.icone.type or 'fas'
            end
             local iconName = config.icone.nom or 'info-circle'
            iconSpan:addClass(iconType):addClass('fa-' .. iconName)
        elseif type(config.icone) == 'string' then
            -- Image personnalisée
            iconSpan:wikitext(string.format('[[Fichier:%s|20px|link=]]', config.icone))
         end
         end
     end
     end
      
      
     -- Titre
    return categoryString
     local titre = type(config.titre) == 'function' and config.titre(args) or config.titre
end
     headerContent:tag('span')
 
         :addClass('infobox-title')
function p.build(args, config, frame)
         :wikitext(titre)
    if not args.nom or args.nom == '' then
        return '<div class="error">Nom requis pour l\'infobox</div>'
    end
 
     -- Validation du champ type
     local typeValidation = validateTypeField(args, config)
    if not typeValidation.valid then
        return '<div class="error">' .. typeValidation.error .. '</div>'
     end
 
    local root = mw.html.create('div')
         :addClass('infobox-table')
         :attr('role', 'table')


     -- Image si définie
     -- En-tête avec nom
     if config.image then
     local header = mw.html.create('div')
         local imageData = type(config.image) == 'function' and config.image(args) or config.image
         :addClass('infobox-header')
         if imageData and imageData.nom then
   
            local imageDiv = root:tag('div'):addClass('infobox-image')
    if args.image and args.image ~= '' then
            local taille = imageData.taille or '300px'
         local imageDiv = mw.html.create('div')
            local legende = imageData.legende
            :addClass('infobox-image')
           
       
            -- Construction du wikitext pour l'image
        local imageSize = args.tailleimage or '280px'
            local imageWikitext = string.format('[[Fichier:%s|%s|center', imageData.nom, taille)
        local imageCode = string.format('[[Fichier:%s|%s|center', args.image, imageSize)
            if legende and legende ~= '' then
        if args.legende and args.legende ~= '' then
                imageWikitext = imageWikitext .. '|' .. legende
            imageCode = imageCode .. '|' .. args.legende
            end
            imageWikitext = imageWikitext .. ']]'
           
            imageDiv:wikitext(imageWikitext)
         end
         end
        imageCode = imageCode .. ']]'
       
        imageDiv:wikitext(imageCode)
        header:node(imageDiv)
     end
     end


     -- Corps de l'infobox avec les sections
     -- Titre sans icône (icône déplacée en bas à droite)
     local body = root:tag('div'):addClass('infobox-body')
     local titleDiv = mw.html.create('div')
        :addClass('infobox-title')
        :wikitext(args.nom)
      
      
     -- Variable pour suivre si les coordonnées ont été affichées
     header:node(titleDiv)
 
    if args.soustitre and args.soustitre ~= '' then
        local subtitleDiv = mw.html.create('div')
            :addClass('infobox-subtitle')
            :wikitext(args.soustitre)
        header:node(subtitleDiv)
    end
 
    root:node(header)
 
    -- Corps avec les champs
    local body = mw.html.create('div')
        :addClass('infobox-body')
 
    -- Vérifier si on a des coordonnées complètes
    local mapLink = buildMapLink(args)
     local coordinatesDisplayed = false
     local coordinatesDisplayed = false
   
 
     if config.sections then
     for _, section in ipairs(config.sections or {}) do
        for _, section in ipairs(config.sections) do
        -- Les lignes de la section sont accumulées ici et intégrées au body seulement
            -- Créer la section seulement si elle a des champs à afficher
        -- si au moins une a du contenu — sinon le section-title reste orphelin (ex. une
            local hasVisibleFields = false
        -- section "Sous-pages" ou "Compatibilité" entièrement vide sur cette page).
            for _, champ in ipairs(section.champs) do
        local sectionRows = {}
                if args[champ.cle] and args[champ.cle] ~= '' then
 
                    hasVisibleFields = true
        for _, champ in ipairs(section.champs or {}) do
                    break
             -- Gestion spéciale pour les coordonnées
                end
             if mapLink and not coordinatesDisplayed and (champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z' or champ.cle == 'worldname' or champ.cle == 'coordonnees') then
            end
                local rowDiv = mw.html.create('div')
           
                    :addClass('infobox-row')
             -- Vérifier aussi les coordonnées groupées
               
             if not coordinatesDisplayed and args.x and args.y and args.z then
                 local labelDiv = mw.html.create('div')
                for _, champ in ipairs(section.champs) do
                    :addClass('infobox-label')
                    if champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z' then
                    :wikitext('Coordonnées')
                        hasVisibleFields = true
               
                        break
                 local valueDiv = mw.html.create('div')
                    end
                    :addClass('infobox-value')
                 end
                    :wikitext(mapLink)
            end
           
            if hasVisibleFields then
                 local sectionDiv = body:tag('div'):addClass('infobox-section')
                  
                  
                 -- Titre de section
                 rowDiv:node(labelDiv):node(valueDiv)
                if section.titre then
                table.insert(sectionRows, rowDiv)
                    sectionDiv:tag('div')
                 coordinatesDisplayed = true
                        :addClass('infobox-section-title')
                        :wikitext(section.titre)
                 end
                  
                  
                -- Vérifier si cette section contient les coordonnées (x, y, z)
            -- Traitement normal des autres champs (ignorer x, y, z, worldname s'ils sont déjà affichés comme coordonnées)
                local hasCoordinates = false
            elseif not (mapLink and (champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z' or champ.cle == 'worldname' or champ.cle == 'mapname' or champ.cle == 'zoom')) then
                if not coordinatesDisplayed and args.x and args.y and args.z then
                local rawValue = args[champ.cle]
                    for _, champ in ipairs(section.champs) do
                        if champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z' then
                            hasCoordinates = true
                            break
                        end
                    end
                end
                  
                  
                 -- Si la section contient les coordonnées, les afficher groupées
                 -- Si le champ a une valeur OU une fonction process qui peut générer une valeur
                 if hasCoordinates and not coordinatesDisplayed then
                 if (rawValue and rawValue ~= '') or champ.process then
                    -- Vérifier si on peut créer un lien (worldname obligatoire)
                     local finalValue = rawValue
                     local canCreateLink = args.worldname and args.worldname ~= ''
                      
                      
                     local labelDiv = mw.html.create('div')
                     -- Appliquer la fonction process si elle existe
                        :addClass('infobox-label')
                     if champ.process and type(champ.process) == 'function' then
                        :wikitext("'''Coordonnées'''")
                         finalValue = champ.process(rawValue, args, frame)
                   
                    local valueDiv = mw.html.create('div')
                        :addClass('infobox-value')
                   
                     if canCreateLink then
                        -- Construction du lien avec worldname
                        local mapname = args.mapname and args.mapname ~= '' and args.mapname or 'flat'
                        local zoom = args.zoom and args.zoom ~= '' and args.zoom or '4'
                       
                        local mapUrl = string.format(
                            'https://map.nefald.fr/?worldname=%s&mapname=%s&zoom=%s&x=%s&y=%s&z=%s',
                            mw.uri.encode(args.worldname),
                            mw.uri.encode(mapname),
                            mw.uri.encode(zoom),
                            mw.uri.encode(args.x),
                            mw.uri.encode(args.y),
                            mw.uri.encode(args.z)
                        )
                       
                        local displayText = string.format('%s, %s, %s', args.x, args.y, args.z)
                        valueDiv:wikitext(string.format('[%s %s]', mapUrl, displayText))
                    else
                        -- Affichage simple sans lien si worldname manque
                         local displayText = string.format('%s, %s, %s', args.x, args.y, args.z)
                        valueDiv:wikitext(displayText)
                     end
                     end
                      
                      
                     local rowDiv = mw.html.create('div'):addClass('infobox-row')
                     -- N'afficher que si on a une valeur finale
                    rowDiv:node(labelDiv):node(valueDiv)
                    if finalValue and finalValue ~= '' then
                    sectionDiv:node(rowDiv)
                        local rowDiv = mw.html.create('div')
                    coordinatesDisplayed = true
                            :addClass('infobox-row')
                end
 
               
                        local valueDiv = mw.html.create('div')
                -- Champs de la section
                            :addClass('infobox-value')
                for _, champ in ipairs(section.champs) do
 
                    local value = args[champ.cle]
                        -- Champ sans label (ex. liste pleine largeur type Sous-pages) :
                   
                        -- pas de colonne label, la valeur devient seule enfant flex du
                    -- Ignorer les coordonnées individuelles si elles ont été affichées groupées
                        -- row et occupe donc naturellement 100% de la largeur.
                    if coordinatesDisplayed and (champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z') then
                        if champ.label and champ.label ~= '' then
                        -- Ne rien faire, on a déjà affiché les coordonnées groupées
                            local labelDiv = mw.html.create('div')
                    elseif value and value ~= '' then
                                :addClass('infobox-label')
                        local rowDiv = sectionDiv:tag('div'):addClass('infobox-row')
                                :wikitext(champ.label)
                       
                             rowDiv:node(labelDiv)
                        -- Label
                        end
                        rowDiv:tag('div')
                             :addClass('infobox-label')
                            :wikitext("'''" .. champ.label .. "'''")
                          
                          
                         -- Valeur
                         -- Formatage spécial pour le champ "type"
                        local valueDiv = rowDiv:tag('div'):addClass('infobox-value')
                         if champ.cle == 'type' and typeValidation.displayValue then
                       
                             valueDiv:wikitext(typeValidation.displayValue)
                        -- Traiter la valeur (fonction ou chaîne)
                         if type(champ.format) == 'function' then
                             valueDiv:wikitext(champ.format(value, args))
                         else
                         else
                             valueDiv:wikitext(value)
                             valueDiv:wikitext(finalValue)
                         end
                         end
                        rowDiv:node(valueDiv)
                        table.insert(sectionRows, rowDiv)
                     end
                     end
                 end
                 end
             end
             end
         end
         end
    end


    return tostring(root)
        if #sectionRows > 0 then
end
            if section.titre then
 
                local sectionDiv = mw.html.create('div')
-- Fonction pour valider le type
                    :addClass('infobox-section-title')
function p.validateType(args, config)
                    :wikitext(section.titre)
    if not config.typeField then
                body:node(sectionDiv)
        return true, nil
            end
    end
             for _, rowDiv in ipairs(sectionRows) do
   
                body:node(rowDiv)
    local typeValue = args.type
            end
    if not typeValue or typeValue == '' then
        if config.typeField.required then
             return false, "Le champ 'type' est obligatoire"
         end
         end
        return true, nil
     end
     end
   
    -- Vérifier si le type est dans les valeurs autorisées
    if config.typeField.allowedValues then
        local typeConfig = config.typeField.allowedValues[typeValue:lower()]
        if not typeConfig then
            return false, config.typeField.errorMessage or "Type non reconnu"
        end
        return true, typeConfig
    end
   
    return true, nil
end


-- Fonction pour générer les catégories
    root:node(body)
function p.generateCategories(args, config, typeConfig)
    if not config.categories then
        return ''
    end
      
      
     return config.categories(args, typeConfig)
     -- Traiter l'icône APRÈS avoir construit tout le HTML
end
     local finalResult = tostring(root)
 
-- Fonction principale appelée par {{#invoke:}}
function p.main(frame)
     local args = getArgs(frame, {parentFirst = true})
      
      
     -- Récupérer le type d'infobox depuis les arguments du frame
     if config.icone then
    local infoboxType = frame.args[1] or frame.args.type
        local iconeConfig = config.icone
    if not infoboxType or infoboxType == '' then
        if type(iconeConfig) == 'function' then
         return '<div class="error">Type d\'infobox requis</div>'
            iconeConfig = iconeConfig(args) or {}
    end
         end
   
        local iconeType = iconeConfig.type or 'fas'
    -- Charger la configuration appropriée
        local iconeNom = iconeConfig.nom
    local configModule = 'Module:Infobox/Configs/' .. infoboxType
       
    local success, config = pcall(require, configModule)
        -- Utiliser mw.getCurrentFrame() si frame n'est pas disponible
   
        local currentFrame = frame or mw.getCurrentFrame()
    if not success then
        local iconeContent = '{{#' .. iconeType .. ':' .. iconeNom .. '}}'
         return '<div class="error">Configuration introuvable pour : ' .. infoboxType .. '</div>'
       
    end
        if currentFrame then
   
            iconeContent = currentFrame:preprocess(iconeContent)
    -- Valider le type si nécessaire
        end
    local isValid, typeConfig = p.validateType(args, config)
       
    if not isValid then
        -- Créer la div d'icône séparément et l'ajouter au résultat
        return '<div class="error">' .. typeConfig .. '</div>'
         local iconeHtml = string.format(
            '<div class="infobox-icon-bottom">%s</div>',
            iconeContent
        )
       
        -- Injecter l'icône juste avant la fermeture de la div principale
        finalResult = finalResult:gsub('</div>$', iconeHtml .. '</div>')
     end
     end
      
      
    -- Générer l'infobox
     -- Ajouter les catégories automatiques
    local infobox = p.createInfobox(config, args)
     local categories = addCategories(args, config, frame)
   
     -- Ajouter les catégories
     local categories = p.generateCategories(args, config, typeConfig)
      
      
     return infobox .. categories
     return finalResult .. categories
end
end


return p
return p

Dernière version du 3 août 2026 à 09:32

La documentation pour ce module peut être créée à Module:Infobox/Core/doc

local p = {}
local getArgs = require('Module:Arguments').getArgs

-- Fonction pour générer le lien de carte dynamique
local function buildMapLink(args)
    -- Vérifier les champs obligatoires
    if not args.x or args.x == '' then return nil end
    if not args.y or args.y == '' then return nil end
    if not args.z or args.z == '' then return nil end
    if not args.worldname or args.worldname == '' then return nil end
    
    -- Valeurs par défaut pour les champs optionnels
    local mapname = args.mapname or 'flat'
    local zoom = args.zoom or '4'
    
    -- Construction de l'URL
    local url = string.format(
        'https://map.nefald.fr/?worldname=%s&mapname=%s&zoom=%s&x=%s&y=%s&z=%s',
        mw.uri.encode(args.worldname, 'PATH'),
        mw.uri.encode(mapname, 'PATH'),
        zoom,
        args.x,
        args.y,
        args.z
    )
    
    -- Retourner le lien formaté
    return string.format('[%s %s, %s, %s]', url, args.x, args.y, args.z)
end

-- Fonction de validation du champ "type"
local function validateTypeField(args, config)
    local typeValue = args.type or args['type']
    
    -- Si pas de configuration pour le type, pas de validation
    if not config.typeField then
        return { valid = true }
    end
    
    -- Si le champ est requis et vide
    if config.typeField.required and (not typeValue or typeValue == '') then
        return { 
            error = 'Le champ "type" est obligatoire.',
            valid = false 
        }
    end
    
    -- Si pas de valeur, mais pas requis, c'est valide
    if not typeValue or typeValue == '' then
        return { valid = true }
    end
    
    -- Nettoyer la valeur
    local cleanType = mw.text.trim(mw.ustring.lower(typeValue))
    
    -- Vérifier si la valeur est dans la liste autorisée
    if config.typeField.allowedValues and not config.typeField.allowedValues[cleanType] then
        local errorMsg = config.typeField.errorMessage or 'Valeur de type non autorisée : ' .. typeValue
        return { 
            error = errorMsg,
            valid = false 
        }
    end
    
    return { 
        valid = true,
        cleanValue = cleanType,
        displayValue = config.typeField.allowedValues and config.typeField.allowedValues[cleanType] and config.typeField.allowedValues[cleanType].display or typeValue
    }
end

-- Fonction pour gérer la catégorisation automatique
local function addCategories(args, config, frame)
    -- Vérifier qu'on est dans le namespace principal (0) ou namespace plugin (3000)
    local title = mw.title.getCurrentTitle()
    if title.namespace ~= 0 and title.namespace ~= 3000 then
        return ''
    end
    
    local categoryString = ''
    
    -- Nouvelles catégories configurables (fonction categories)
    if type(config.categories) == 'function' then
        local cats = config.categories(args, config, frame)
        if cats then
            for _, cat in ipairs(cats) do
                categoryString = categoryString .. '[[Catégorie:' .. cat .. ']]'
            end
        end
    end
    
    return categoryString
end

function p.build(args, config, frame)
    if not args.nom or args.nom == '' then
        return '<div class="error">Nom requis pour l\'infobox</div>'
    end

    -- Validation du champ type
    local typeValidation = validateTypeField(args, config)
    if not typeValidation.valid then
        return '<div class="error">' .. typeValidation.error .. '</div>'
    end

    local root = mw.html.create('div')
        :addClass('infobox-table')
        :attr('role', 'table')

    -- En-tête avec nom
    local header = mw.html.create('div')
        :addClass('infobox-header')
    
    if args.image and args.image ~= '' then
        local imageDiv = mw.html.create('div')
            :addClass('infobox-image')
        
        local imageSize = args.tailleimage or '280px'
        local imageCode = string.format('[[Fichier:%s|%s|center', args.image, imageSize)
        if args.legende and args.legende ~= '' then
            imageCode = imageCode .. '|' .. args.legende
        end
        imageCode = imageCode .. ']]'
        
        imageDiv:wikitext(imageCode)
        header:node(imageDiv)
    end

    -- Titre sans icône (icône déplacée en bas à droite)
    local titleDiv = mw.html.create('div')
        :addClass('infobox-title')
        :wikitext(args.nom)
    
    header:node(titleDiv)

    if args.soustitre and args.soustitre ~= '' then
        local subtitleDiv = mw.html.create('div')
            :addClass('infobox-subtitle')
            :wikitext(args.soustitre)
        header:node(subtitleDiv)
    end

    root:node(header)

    -- Corps avec les champs
    local body = mw.html.create('div')
        :addClass('infobox-body')

    -- Vérifier si on a des coordonnées complètes
    local mapLink = buildMapLink(args)
    local coordinatesDisplayed = false

    for _, section in ipairs(config.sections or {}) do
        -- Les lignes de la section sont accumulées ici et intégrées au body seulement
        -- si au moins une a du contenu — sinon le section-title reste orphelin (ex. une
        -- section "Sous-pages" ou "Compatibilité" entièrement vide sur cette page).
        local sectionRows = {}

        for _, champ in ipairs(section.champs or {}) do
            -- Gestion spéciale pour les coordonnées
            if mapLink and not coordinatesDisplayed and (champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z' or champ.cle == 'worldname' or champ.cle == 'coordonnees') then
                local rowDiv = mw.html.create('div')
                    :addClass('infobox-row')
                
                local labelDiv = mw.html.create('div')
                    :addClass('infobox-label')
                    :wikitext('Coordonnées')
                
                local valueDiv = mw.html.create('div')
                    :addClass('infobox-value')
                    :wikitext(mapLink)
                
                rowDiv:node(labelDiv):node(valueDiv)
                table.insert(sectionRows, rowDiv)
                coordinatesDisplayed = true
                
            -- Traitement normal des autres champs (ignorer x, y, z, worldname s'ils sont déjà affichés comme coordonnées)
            elseif not (mapLink and (champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z' or champ.cle == 'worldname' or champ.cle == 'mapname' or champ.cle == 'zoom')) then
                local rawValue = args[champ.cle]
                
                -- Si le champ a une valeur OU une fonction process qui peut générer une valeur
                if (rawValue and rawValue ~= '') or champ.process then
                    local finalValue = rawValue
                    
                    -- Appliquer la fonction process si elle existe
                    if champ.process and type(champ.process) == 'function' then
                        finalValue = champ.process(rawValue, args, frame)
                    end
                    
                    -- N'afficher que si on a une valeur finale
                    if finalValue and finalValue ~= '' then
                        local rowDiv = mw.html.create('div')
                            :addClass('infobox-row')

                        local valueDiv = mw.html.create('div')
                            :addClass('infobox-value')

                        -- Champ sans label (ex. liste pleine largeur type Sous-pages) :
                        -- pas de colonne label, la valeur devient seule enfant flex du
                        -- row et occupe donc naturellement 100% de la largeur.
                        if champ.label and champ.label ~= '' then
                            local labelDiv = mw.html.create('div')
                                :addClass('infobox-label')
                                :wikitext(champ.label)
                            rowDiv:node(labelDiv)
                        end
                        
                        -- Formatage spécial pour le champ "type"
                        if champ.cle == 'type' and typeValidation.displayValue then
                            valueDiv:wikitext(typeValidation.displayValue)
                        else
                            valueDiv:wikitext(finalValue)
                        end

                        rowDiv:node(valueDiv)
                        table.insert(sectionRows, rowDiv)
                    end
                end
            end
        end

        if #sectionRows > 0 then
            if section.titre then
                local sectionDiv = mw.html.create('div')
                    :addClass('infobox-section-title')
                    :wikitext(section.titre)
                body:node(sectionDiv)
            end
            for _, rowDiv in ipairs(sectionRows) do
                body:node(rowDiv)
            end
        end
    end

    root:node(body)
    
    -- Traiter l'icône APRÈS avoir construit tout le HTML
    local finalResult = tostring(root)
    
    if config.icone then
        local iconeConfig = config.icone
        if type(iconeConfig) == 'function' then
            iconeConfig = iconeConfig(args) or {}
        end
        local iconeType = iconeConfig.type or 'fas'
        local iconeNom = iconeConfig.nom
        
        -- Utiliser mw.getCurrentFrame() si frame n'est pas disponible
        local currentFrame = frame or mw.getCurrentFrame()
        local iconeContent = '{{#' .. iconeType .. ':' .. iconeNom .. '}}'
        
        if currentFrame then
            iconeContent = currentFrame:preprocess(iconeContent)
        end
        
        -- Créer la div d'icône séparément et l'ajouter au résultat
        local iconeHtml = string.format(
            '<div class="infobox-icon-bottom">%s</div>',
            iconeContent
        )
        
        -- Injecter l'icône juste avant la fermeture de la div principale
        finalResult = finalResult:gsub('</div>$', iconeHtml .. '</div>')
    end
    
    -- Ajouter les catégories automatiques
    local categories = addCategories(args, config, frame)
    
    return finalResult .. categories
end

return p
Les témoins (''cookies'') nous aident à fournir nos services. En utilisant nos services, vous acceptez notre utilisation de témoins.