Module:Utilisateur:François Melchior/Test

Un livre de Wikilivres.

La documentation pour ce module peut être créée à Module:Utilisateur:François Melchior/Test/Documentation

local M = {}

local sprint_r = require("Module:Utilisateur:François Melchior/Utile").sprint_r

function M.hello()
    return "Hello!"
end

function M.helloDiv()
    return "<div>Hello!</div>"
end

function M.testTemplate(frame)
    return frame:expandTemplate{title = "Utilisateur:François Melchior/Bac à sable/Lua/t5/include" .. (frame.args[1] or "")}
end

function M.testTemplate2(frame)
    return "^" .. frame:expandTemplate{title = "Utilisateur:François Melchior/Bac à sable/Lua/t5/include" .. (frame.args[1] or "")} .. "$"
end

function M.testTemplateTrim(frame)
    return mw.text.trim(frame:expandTemplate{title = "Utilisateur:François Melchior/Bac à sable/Lua/t5/include" .. (frame.args[1] or "")})
end

function M.testTemplateTrim2(frame)
    return "^" .. mw.text.trim(frame:expandTemplate{title = "Utilisateur:François Melchior/Bac à sable/Lua/t5/include" .. (frame.args[1] or "")}) .. "$"
end

function M.testTemplate_print_r(frame)
    return sprint_r(frame:expandTemplate{title = "Utilisateur:François Melchior/Bac à sable/Lua/t5/include" .. (frame.args[1] or "")})
end
----------------------------------------------------------
function M.args(frame)
    local ret
    for k, v in pairs(frame.args) do
        v = v .. " [" .. string.len(v) .. "]"
        if ret then
            ret = ret .. " | " .. k .. " = " .. v
        else
            ret = k .. " = " .. v
        end
    end
    return ret
end

function M.parentArgs(frame)
    -- local ThisFrame = mw.getCurrentFrame
    local parentArgs = frame:getParent().args
    local ret
    local k
    local v
    for k, v in pairs(parentArgs) do
        v = v .. " [" .. string.len(v) .. "]"
        if ret then
            ret = ret .. " | " .. k .. " = " .. v
        else
            ret = k .. " = " .. v
        end
    end
    return ret -- ParentFrame.args --"Hello!"
end

function M.testeArgs(frame)
    local taille --= table.maxn(frame.args)
    for i = 1, 999 do
        if not frame.args[i] then
            taille = i - 1
            break
        end
    end
    local dernier = frame.args[taille]
    frame.args[taille] = nil
    return M.args(frame) ..
           frame:expandTemplate{title = dernier, args = mw.clone(frame.args)}
end

--[[function M["paramètres"](frame)
    local parentArgs = frame:getParent().args
    local decoNom = frame.args["déco nom"] or ""
    local decoValeur = frame.args["déco valeur"] or ""
    local separateur = frame.args["sépareteur"] or ""
    local ret
    
    for k, v in pairs(parentArgs) do
        v = v .. " [" .. string.len(v) .. "]"
        if ret then
            ret = ret .. separateur .. k .. " = " .. v
        else
            ret = k .. " = " .. v
        end
    end
    
    return ret
end]]
----------------------------------------------------------
function M.umodule(frame)
    package.preload["umodule"] = function()
        local m
        m = frame:expandTemplate{ title = "Utilisateur:François Melchior/main.lua", args = {} }
        
        return m
    end
    -- require("umodule")
    
    -- return "<pre>\n" .. mw.title.new("Utilisateur:François Melchior/Sommaire"):getContent() .. "\n</pre>"
    return "<pre>\n" .. frame:expandTemplate{ title = "Utilisateur:François Melchior/Sommaire", args = {} } .. "\n</pre>"
end
----------------------------------------------------------
function M.patterns(s)
    local pattern = "^(%+*)(%!?)%s*((%[*)([^|%]]+)|?([^|%]]*)(%]*))%s*$"
    return string.match(s, pattern)
end
----------------------------------------------------------
--[[local function icombine(t1, t2)
    local i = #t1
    for k, v in ipairs(t2) do
        i = i + 1
        t1[i] = v
    end
end]]

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

local livre = require("Module:Livre")

-- pour la transition (temporaire)
local function contenuDeSommaire_tmp(frame, parentArgs, appelleStyle)
    local ret = {}
    if parentArgs["titre"] and mw.text.trim(parentArgs["titre"]) == "" then parentArgs["titre"] = nil end
    parentArgs["partie"] = "début"
    if not parentArgs["base"] or mw.text.trim(parentArgs["base"]) == "" then parentArgs["base"] = string.sub(livre.base(),1,-2) end
    parentArgs.livre, parentArgs.accueil, parentArgs.sommaire = appelleStyle.livre, appelleStyle.accueil, appelleStyle.sommaire
    table.insert(ret, appelleStyle.go(parentArgs))
    
    local sauveNumero10 = parentArgs[10]
    
    for i = 1, 91, 10 do
        parentArgs["précédent"] = parentArgs[i - 1] or ""
        parentArgs["suivant"] = parentArgs[i + 10] or ""
        parentArgs["numéro"] = tostring(i)
        parentArgs[1], parentArgs[2], parentArgs[3], parentArgs[4], parentArgs[5],
            parentArgs[6], parentArgs[7], parentArgs[8], parentArgs[9], parentArgs[10]
            = parentArgs[i], parentArgs[i + 1], parentArgs[i + 2], parentArgs[i + 3], parentArgs[i + 4],
                parentArgs[i + 5], parentArgs[i + 6], parentArgs[i + 7], parentArgs[i + 8], parentArgs[i + 9]
        
        table.insert(ret, frame:expandTemplate{title = "Contenu de sommaire/Liste", args = parentArgs})
        
        if i == 1 then parentArgs[10] = sauveNumero10 end
    end
    
    parentArgs["partie"] = "fin"
    table.insert(ret, appelleStyle.go(parentArgs))
    
    return table.concat(ret)
end

local _
--local chapitreSuivant -- Fonction (closure) définie dans contenuDeSommaire.
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 = {--[[{numero = 1}]]} --{[0] = {texte = false, page = false}}
            i, j = 1, 1
            pagePrecedente = ""
            niveau = 1
        end
 
        while true do repeat
            --[[ local entree, avancement, image = chapitreSuivant() ]]
            
            local entree = args[j]
            if not entree then
            	entree = next(liensAutomatiques)
            	if not entree then return chapitres end
            	--[mieux avec??] liensAutomatiques[entree] = nil
            	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
            
            --nouveauNiveau = #(nouveauNiveau or "") + 1 -- On compte le nombre de "+".
            
            --[=[ Essaye de découper en différentes parties.
            local nouveauNiveau, sousTitre, complet, ouvrants, page, affiche, fermants
                    = string.match(entree, "^(%+*)(%!?)%s*((%[*)([^|%]]+)|?([^|%]]*)(%]*))%s*$") ]=]
            
            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 not page then
                	chapitres[iPrecedent].suivant = 0
                	return chapitres
                end]]
                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 "" --style="haut"
    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
        styleComplet = "Utilisateur:François Melchior/Modèle:Contenu de sommaire/Style " .. style
    end
    
    if mw.title.new(styleComplet).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
    
    -- Table-objet pour les appels au style.
    local appelleStyle = {
        go = function (args) return frame:expandTemplate{title = styleComplet, args = args} end,
        livre = livre.titleParts(baseDuLivre, 1, -2), -- (-2) pour le "/".
        --[[image = parentArgs.image,
        titre = parentArgs.titre ~= "" and parentArgs.titre or false,]]
        accueil = livre.accueil(), -- TODO: À améliorer dans Module:Livre (do some caching)
        sommaire = baseDuLivre .. "Sommaire"
        }
    
    -- Traitement des options.
    for nom, valeur in pairs(parentArgs) do if valeur ~="" then
        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
        --[[if valeur = "avec liens automatiques" and (option =  "option1" or option =  "option2" or option =  "option3" or option =  "option4") then
            appelleStyle[option] = nil end -- Temporaire (pour transition)]]
    end end
    
    if appelleStyle["option titre"] == "~" then appelleStyle["option titre"] = appelleStyle.livre end
    
    --[[if appelleStyle.option1 then -- Temporaire (pour transition)
        if appelleStyle.option2 then
            if appelleStyle.option3 then
                if appelleStyle.option4 then
                else appelleStyle["sans liens automatiques"] = "1" ]]
    
    
--[[
    -- On crée une fonction (closure) pour les appels au style.
    -- Note: les arguments ajoutés au fur et à mesure ne sont effacés que si on leur donnant la valeur "false".
    local appelleStyle
    do
        local callParams = {title = styleComplet, args = {}}
        callParams.args.base = baseDuLivre
        callParams.args.livre = livre.titleParts(baseDuLivre, 1, -2) -- (-2) pour le "/".
        callParams.args.image = parentArgs.image
        callParams.args.titre = parentArgs.titre ~= "" and parentArgs.titre
                                or false --callParams.args.livre
        callParams.args.accueil = livre.accueil() -- TODO: À améliorer dans Module:Livre (do some caching)
        callParams.args.sommaire = baseDuLivre .. "Sommaire"
        
        -- 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 callParams.args[option] = valeur end
        end
        
        -- La fonction elle-même
        function appelleStyle(argsEnPlus, pasAppeler)
            for k, v in pairs(argsEnPlus) do
                if v == false then
                    callParams.args[k] = nil
                else callParams.args[k] = v end
            end
            return pasAppeler and "" or frame:expandTemplate(callParams)
                -- "" et pas nil car pourrait poser problème dans une table.
        end
    end ]]
    
    -- pour la transition (temporaire)
    if parentArgs["complexe"] or parentArgs["option1"] or parentArgs["option2"] or parentArgs["option3"] or parentArgs["option4"] then
        return contenuDeSommaire_tmp(frame, mw.clone(parentArgs), appelleStyle)
        -- mw.clone car certains arguments ne passent pas tout seuls:
        -- problème dû à la méta-table de médiawiki ?
    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
    
    --[[ (Fonction utilisée à l'étape suivante — closure également — module-global)
    do
        local i = 1
        function chapitreSuivant()
            local chapitre = parentArgs[i]
            if chapitre then
                i = i + 1
                local suivant = mw.text.trim(parentArgs[i] or "")
                local avancement
                
                if not string.find(suivant, "%D") then
                    -- Champ avancement présent (ne contient que des chiffres ou est vide).
                    if suivant ~= "" then avancement = suivant end
                    i = i + 1
                    suivant = mw.text.trim(parentArgs[i] or "")
                end
                
                local image = string.match(suivant, "^%[%[%s*[Ii][Mm][Aa][Gg][Ee]%s*:%s*([^|]-)%s*%]%]$")
                if image then
                    -- Champ image présent.
                    i = i + 1
                    suivant = mw.text.trim(parentArgs[i] or "")
                    
                    -- Vérifie si on a un champ avancement après le champ image.
                    if not avancement and not string.find(suivant, "%D") then
                        if suivant ~= "" then avancement = suivant end
                        i = i + 1
                    end
                end
                
                return chapitre, avancement, image
            end
        end
    end

    --[ [ (Une seconde fonction utilisée à l'étape suivante — closure également)
    local gereNiveau --TODO: à plat
    do
        local bases = {}
        local baseActuelle = baseDuLivre
        local niveauActuel = 1
        local pagePrecedente
        function gereNiveau(niveau, page)
            local niveauAChange
            if niveau > niveauActuel and pagePrecedente then
                table.insert(bases, baseActuelle) -- Sauve dans la pile.
                baseActuelle = baseActuelle .. pagePrecedente .. "/"
                niveauActuel = niveau
                niveauAChange = "+"
            elseif niveau < niveauActuel then
                baseActuelle = table.remove(bases) -- Restaure.
                niveauActuel = niveau
                niveauAChange = "-"
            end
            pagePrecedente = page -- Pour la prochaine montée en niveau.
            return baseActuelle .. page, niveauAChange
        end
    end ]]
    
    -- Construction d'une table avec les données de tous les chapitres.
    local chapitres = litChapitres(parentArgs, baseDuLivre)
    
--do return sprint_r(chapitres) end
--liensAutomatiques={}
    --[=[ Ajout des liens automatiques si nécessaire.
    do
        local i = #chapitres
        local n = 0
        --[[local niveauAChange
        local dernierNiveau1 = 0
        
        for j = i, 1, -1 do
            if chapitres[j].niveau == 1 then
                n = chapitres[j].numero
                dernierNiveau1 = j
                if j ~= i then niveauAChange = 1 - chapitres[i].niveau end
                break
            end
        end]]
        
        -- Trouver le dernier chapitre de niveau 1.
        local dernierNiveau1 = i
        while chapitres[dernierNiveau1].niveau ~= 1 do
            dernierNiveau1 = chapitres[dernierNiveau1].parent
        end
        n = chapitres[dernierNiveau1].numero
        local niveauAChange = dernierNiveau1 ~= i and 1 - chapitres[dernierNiveau1].niveau or nil
        
        for page in pairs(liensAutomatiques) do
            i = i + 1
            n = n + 1
            local pageComplete = baseDuLivre .. page
            if siLiensAuto or mw.title.new(pageComplete).exists then
                chapitres[i] = {
                        texte = page, page = pageComplete, numero = n, niveau = 1, auto = "auto",
                        precedent = dernierNiveau1 or i - 1, suivant = i + 1}
                if niveauAChange then
                    chapitres[i].niveauAChange = niveauAChange
                    niveauAChange = nil
                end
                dernierNiveau1 = nil
            end
        end
    end]=]
    
--do return "<pre>\n" .. sprint_r(chapitres) .. "\n</pre>" end
    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
----------------------------------------------------------
function M.right(frame)
    local texte = frame.args[1] or ""
    local n = frame.args[2] or -1
    return mw.ustring.sub(texte, -n)
end

M.droite = M.right -- alias
--M.str_right = M.strRight

function M.left(frame)
    local texte = frame.args[1] or ""
    local n = frame.args[2] or -1
    return mw.ustring.sub(texte, 1, n)
end

M.gauche = M.left -- alias
--M.str_left = M.strLeft

function M.compare(frame)
    local debut = frame.args["début"] or frame.args["begin"] or 1
    local fin = frame.args["fin"] or frame.args["end"] or -1
    local texte1 = mw.ustring.sub(frame.args[1] or "", debut, fin)
    local texte2, siOui, siNon = frame.args[2] or "", frame.args[3] or "", frame.args[4] or ""
    
    return tostring(string.gsub(texte1 == texte2 and siOui or siNon, "{{_}}", {["{{_}}"] = texte1}))
        -- Utilisation d'une table pour ne pas avoir de problèmes de "%"
        -- Note: tostring car gsub renvoie 2 valeurs
end

return M