Ce module contient la fonction suivante :

contenuDeSommaire
cœur fonctionnel du modèle {{contenu de sommaire}} ; il lit directement les paramètres de celui-ci.
local M = {}

local function erreur(msg)
    return mw.getCurrentFrame():expandTemplate{title = "err", args = {msg}}
end

local _
local livre = require("Module:Livre")
local liensAutomatiques
local aPlat

-- Fonction accessoire pour contenuDeSommaire
local litChapitres
do
    local args, chapitres, i, j
    function litChapitres(args_init, base, niveau, parent, pagePrecedente)
        local n = 1
        local sousTitreEnAttente, auto
        
        if args_init then
            args = args_init
            chapitres = {}
            i, j = 1, 1
            pagePrecedente = ""
            niveau = 1
        end
 
        while true do repeat
            local entree = args[j]
            if not entree then
                entree = next(liensAutomatiques)
                if not entree then return chapitres end
                auto = "auto"
            end
            
            -- Essaye de découper en différentes parties.
            local nouveauNiveau, sousTitre, complet, ouvrants, lien, fermants
                    = string.match(entree, "^(%+*)(%!?)%s*((%[*)(.-)(%]*))%s*$")
            
            if sousTitre == "!" then
                -- Si c'est un sous-titre, il sera associé au chapitre suivant.
                sousTitreEnAttente = mw.text.trim(complet)
                if chapitres[i] then i, n = i + 1, n + 1 end
                  -- Passage au chapitre suivant si nécessaire
                j = j + 1 -- Passage à l'entrée suivante.
                break
            end
            
            if nouveauNiveau == "" then
                if complet == "" then
                    if chapitres[i] then i, n = i + 1, n + 1 end
                      -- Passage au chapitre suivant si nécessaire
                    j = j + 1 -- Passage à l'entrée suivante.
                    break
                end
                nouveauNiveau = 1
            else
                nouveauNiveau = #nouveauNiveau + 1 -- On compte le nombre de "+".
                if chapitres[i] then i, n = i + 1, n + 1 end
                    -- Ne peut être ni image, ni avancement, on est donc au chapitre suivant.
            end
            
            local page
            
            if ouvrants == "[[" and fermants == "]]" then
                local x = string.find(lien, "|", 1, true)
                page = mw.text.trim(x and string.sub(lien, 1, x - 1) or lien)
                
                if chapitres[i] and not chapitres[i].image then
                    local image = string.match(page, "^[Ii][Mm][Aa][Gg][Ee]%s*:%s*(.*)$")
                    if image then
                        chapitres[i].image = image
                        j = j + 1 -- Passage à l'entrée suivante.
                        if chapitres[i].avancement then
                            i, n = i + 1, n + 1 -- Passage au chapitre suivant.
                        end
                        break
                    end
                    i, n = i + 1, n + 1 -- N'était pas une image, on est donc au chapitre suivant.
                end
                affiche = x and mw.text.trim(string.sub(lien, x + 1)) or page
            else
                if chapitres[i] and not chapitres[i].avancement then
                    if not string.find(complet, "%D") then
                        -- Champ avancement présent (ne contient que des chiffres).
                        chapitres[i].avancement = complet
                        j = j + 1 -- Passage à l'entrée suivante.
                        if chapitres[i].avancement then
                            i, n = i + 1, n + 1 -- Passage au chapitre suivant.
                        end
                        break
                    end
                    i, n = i + 1, n + 1 -- N'était pas un champ avancement, on est donc au chapitre suivant.
                end
                page = complet
                affiche = page
                lien = nil
            end
            
            -- Suppression du lien automatique s'il est déjà présent.
            if liensAutomatiques[affiche] then liensAutomatiques[affiche] = nil end
            
            chapitres[i] = {
                numero = n, texte = affiche, page = page, auto = auto,
                niveau = nouveauNiveau, sousTitre = sousTitreEnAttente,
                parent = parent, precedent = i - 1, suivant = i + 1}
            
            sousTitreEnAttente = nil
            j = j + 1 -- Passage à l'entrée suivante.
            
            -- Changement de niveau: appel récursif si on monte, retour à l'appelant si on descend.
            if nouveauNiveau > niveau then
                local iPrecedent = i - 1
                local base = aPlat and base or base .. pagePrecedente .. "/"
                if not lien then chapitres[i].page = base .. page end
                chapitres[i].niveauAChange = "+"
                chapitres[i].numero = 1
                chapitres[i].parent = i - 1
                chapitres[i].precedent = 0
                -- On ne peut monter que d'1 niveau à la fois.
                if nouveauNiveau - niveau > 1 then
                    chapitres[i].texte = erreur("Niveau intermédiaire absent")
                    chapitres[i].page = "Modèle:Contenu de sommaire"
                    return chapitres
                end
                _, page, nouveauNiveau, lien = litChapitres(nil, base, nouveauNiveau, i - 1, lien and pagePrecedente or page)
                if nouveauNiveau ~= niveau then
                    chapitres[iPrecedent].suivant = 0
                    return chapitres--[[nil]], page, nouveauNiveau, lien
                end
                chapitres[i].numero = n
                chapitres[i].parent = parent
                chapitres[i].precedent = iPrecedent
                chapitres[i - 1].suivant = 0
                chapitres[iPrecedent].suivant = i
            elseif nouveauNiveau < niveau then
                chapitres[i].niveauAChange = nouveauNiveau - niveau
                return nil, page, nouveauNiveau, lien
            end
            
            if not lien then
                chapitres[i].page = base .. page
                pagePrecedente = page
            end
        until true end
    end
end

function M.contenuDeSommaire(frame)
    local parentArgs = frame:getParent().args
    local baseDuLivre = livre.base()
    
    -- D'abord vérifier que le style existe
    local style = parentArgs["style"] or ""
    if style == "" then style = "par défaut" end
    
    local styleComplet
    if style == "sommaire personnalisé" then
        styleComplet = baseDuLivre .. "Style du sommaire"
    else styleComplet = "Modèle:Contenu de sommaire/Style " .. style end
    
    local t = mw.title.new(styleComplet)
    if t.id == 0 then
        -- Si le style demandé (ou le style par défaut) n'existe pas, renvoie un message d’erreur.
        return erreur("Erreur: style [[" .. styleComplet .. "|" .. style .. "]] inexistant.")
    end
    if t.namespace == 0 then styleComplet = ":" .. styleComplet end
    
    -- Table-objet pour les appels au style.
    -- (Note: s'utilise elle-même comme métatable à cause de "expandTemplate" qui émet une erreur si il reçoit une fonction parmi ses arguments)
    local appelleStyle = {
        go = function (t) return frame:expandTemplate{title = styleComplet, args = t.__index} end,
        __index = {
          livre = livre.titleParts(baseDuLivre, 1, -2), -- (-2) pour le "/".
          accueil = livre.accueil(), -- TODO: À améliorer dans Module:Livre (do some caching)
          sommaire = baseDuLivre .. "Sommaire"
          }
        }
    setmetatable(appelleStyle, appelleStyle)
    appelleStyle.__newindex = appelleStyle.__index
    
    -- Traitement des options.
    for nom, valeur in pairs(parentArgs) do
        local option, avecStyles, listeDeStyles
                = string.match(nom, "^(option[^:]*[^:%s])%s*(%:?)(.*)$")
        if avecStyles == "" or
           option and string.find("," .. listeDeStyles .. ",", "%,%s*" .. style .. "%s*%,")
        then appelleStyle[option] = valeur end
    end
    
    if appelleStyle["option titre"] == "~" then appelleStyle["option titre"] = appelleStyle.livre end
    
    local trueFalse = {oui = true, ["true"] = true, ["1"] = true,
                       non = false, ["false"] = false, ["0"] = false}
    
    aPlat = trueFalse[parentArgs["à plat"] or ""]
    
    local siLiensAuto = trueFalse[parentArgs["liens automatiques"] or ""]
    if siLiensAuto == false then
        liensAutomatiques = {}
    else
        liensAutomatiques = {Glossaire = 1, Auteurs = 1, Bibliographie = 1, Index = 1, Licence = 1}
        if not siLiensAuto then for page in pairs(liensAutomatiques) do
            if not mw.title.new(baseDuLivre .. page).exists then liensAutomatiques[page] = nil end
        end end
    end
    
    -- Construction d'une table avec les données de tous les chapitres.
    local chapitres = litChapitres(parentArgs, baseDuLivre)
    
    local ret = {} -- texte à retourner.
    local parents = {0}
        -- structure de pile pour conserver les 'parents' précédents.
    local chapitreVide = {texte = false, page = false}
    appelleStyle.partie = "passes"
    
    for passe = 1, tonumber(appelleStyle:go()) or 1 do

        -- Maintenant on peut faire la partie avant les chapitres.
        appelleStyle.partie, appelleStyle.passe, appelleStyle.niveau = "début", passe, 1
        ret[#ret + 1] = appelleStyle:go()
        
        -- Puis faire les chapitres.
        for i, chapitre in ipairs(chapitres) do
            
            if chapitre.niveauAChange then
                if chapitre.niveauAChange == "+" then
                    local parent = chapitres[chapitre.parent]
                    appelleStyle.partie, appelleStyle.niveau, appelleStyle.parent, appelleStyle["page parent"]
                        = "début de liste", chapitre.niveau, parent.texte, parent.page
                    ret[#ret + 1] = appelleStyle:go()
                    parents[chapitre.niveau] = chapitre.parent
                else
                    appelleStyle.partie = "fin de liste"
                    for i = chapitre.niveau - chapitre.niveauAChange - 1, chapitre.niveau, -1 do
                        local parent = chapitres[parents[i]] or chapitreVide
                        ret[#ret + 1] = appelleStyle:go()
                        appelleStyle.niveau, appelleStyle.parent, appelleStyle["page parent"]
                            = i, parent.texte, parent.page
                    end
                end
            end
            
            if chapitre.sousTitre then
                appelleStyle.partie, appelleStyle["sous-titre"]
                    = "sous-titre", chapitre.sousTitre
                ret[#ret + 1] = appelleStyle:go()
            else
                appelleStyle["sous-titre"] = false
            end
            
            local precedent = chapitres[chapitre.precedent] or chapitreVide
            local suivant = chapitres[chapitre.suivant] or chapitreVide
            appelleStyle.partie, appelleStyle.auto, appelleStyle.chapitre, appelleStyle.page,
            appelleStyle["numéro"], appelleStyle.image, appelleStyle.avancement,
            appelleStyle["précédent"], appelleStyle["page précédent"], appelleStyle.suivant, appelleStyle["page suivant"]
                = "chapitre", chapitre.auto, chapitre.texte, chapitre.page,
                   chapitre.numero, chapitre.image or false, chapitre.avancement or false,
                   precedent.texte, precedent.page, suivant.texte, suivant.page
            ret[#ret + 1] = appelleStyle:go()
        end
        
        -- Redescendre au niveau 1 si on n'y est pas.
        appelleStyle.partie = "fin de liste"
        for i = chapitres[#chapitres].niveau - 1, 1, -1 do
            local parent = chapitres[parents[i]] or chapitreVide
            appelleStyle.niveau = i
            ret[#ret + 1] = appelleStyle:go()
            appelleStyle.niveau, appelleStyle.parent, appelleStyle["page parent"]
                = i, parent.texte, parent.page
        end
        
        -- Et terminer par la partie finale.
        appelleStyle.partie, appelleStyle.auto = "fin", false
        ret[#ret + 1] = appelleStyle:go()
    end
    
    return table.concat(ret)
end

return M