Aller au contenu

Module:Taxobox2

De Baripedia

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

--[[
  Module créant une taxobox.
  
  Utilise Module:Taxobox données et Module:Taxobox fonctions.

  Note : le nom est temporaire. Module:Taxobox existe et contient une vieille version destinée à être remplacée.
--]]


-- la table du module
local p = {}

local donnees = require "Module:Taxobox données"
local fnc = require "Module:Taxobox fonctions"


--[[
  Fonction générant la sortie finale. Permet d'inclure le debug éventuel
--]]
function p.prepare_sortie(contenu, db)
    if (donnees.defauts.c_debug) then
        return db .. donnees.defauts.frame:preprocess(contenu)
    else
        return donnees.defauts.frame:preprocess(contenu)
    end
end


--[[
  Fonction principale : traite les paramètres et génère la taxobox.
  Les paramètres sont :
    règne : obligatoire. Sélectionne le thème (couleur, convention typographique, lignes de classification)
    nom vernaculaire : facultatif. Informe la taxobox du ou des noms vernaculaires existants
    …
--]]
function p.taxobox(frame)
    local dg = ""
    
    donnees.defauts.frame = frame -- pour que les fonctions aient accès à la frame courante simplement
    donnees.defauts.pframe = frame:getParent() -- pour que les fonctions aient accès à la frame courante simplement
    
    dg = dg .. "frames préparées.<br/>"
    
    -- on récupère les infos sur l'article où on se trouve
    local curarticle = mw.title.getCurrentTitle()
    if (curarticle.namespace == 0) then -- est-ce un article ou pas
        donnees.defauts.article = true
    else
        donnees.defauts.article = false
    end
    -- le titre courant
    donnees.defauts.titre = curarticle.text
    
    dg = dg .. "namespaces+titre extraits.<br/>"
    
    -- récupération des paramètres
    donnees.defauts.regne = fnc.lit_parametre("charte", donnees.regnes, false, nil)  -- la charte
    if (donnees.defauts.regne == nil) then -- on teste charte, puis règne si marche pas
        donnees.defauts.regne = fnc.lit_parametre("règne", donnees.regnes, false, true)  -- le règne
    end
    donnees.defauts.nom_vernaculaire = fnc.lit_parametre("nom vernaculaire", nil, false, nil)  -- le non vernaculaire
    -- les options permettant de modifier le comportement
    donnees.defauts.c_titre = fnc.lit_parametre("titre", {"oui","non","boîte","auto"}, false, "auto")
    donnees.defauts.c_sous_titre = fnc.lit_parametre("sous-titre", {"oui","non","auto"}, false, "auto")
    donnees.defauts.c_erreurs = fnc.lit_parametre("erreurs", {"oui","non","auto"}, false, "auto")
    donnees.defauts.c_categories = fnc.lit_parametre("catégories", {"en ligne","non","boîte","auto"}, false, "auto")
    donnees.defauts.c_err_categories = fnc.lit_parametre("catégorie erreurs", {"en ligne","non","boîte","auto"}, false, "auto")
    donnees.defauts.c_force_titre = fnc.lit_parametre("force titre", nil, false, nil)
    -- si force titre vide on utilise le titre réel
    if (donnees.defauts.c_force_titre == nil) then
        donnees.defauts.c_force_titre = donnees.defauts.titre
    end

    -- debug
    donnees.defauts.c_raw = fnc.lit_parametre("raw", nil, true, nil)
    donnees.defauts.c_debug =  fnc.lit_parametre("debug", nil, true, nil)

    -- on regarde si une erreur (ou plusieurs) s'est produite durant la récupération des paramètres
    if (donnees.defauts.fatal) then
        return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg)
    end
    
    dg = dg .. "paramètres récupérés.<br/>"

    -- on récupère la structure règne
    donnees.defauts.regne_data = donnees.regnes[donnees.defauts.regne]
    if (donnees.defauts.regne_data == nil) then
        fnc.erreur_normale("Le règne indiqué ''" .. donnees.defauts.regne .. "'' n'est pas reconnu.", "syntaxe")
        return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg)
    end

    -- on analyse les paramètres pour générer la liste des commandes présentes
    local xxx, yyy = fnc.tri_parametres(donnees)
    dg = dg .. yyy
    if (donnees.defauts.fatal) then -- erreur → fatal
        return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg)
    end
    
    dg = dg .. "commandes analysées.<br/>"
    local w = 1
    while (donnees.defauts.commandes[w] ~= nil) do
        local ww = 1
        while (donnees.defauts.commandes[w][ww] ~= nil) do
            dg = dg .. donnees.defauts.commandes[w][ww] .. " "
            ww = ww + 1
        end
        dg = dg .. "<br/>"
        w = w + 1
    end
    dg = dg .. "EOC<br/>"

    -- initialisation de l'état de la taxobox en construction
    donnees.etat = {}
    donnees.etat.ouvert = false -- la taxobox est commencée
    donnees.etat.ferme = false -- la taxobox est terminée
    donnees.etat.ligne = "non commencé" -- le nom de la ligne en cours
    donnees.etat.tbl = 0 -- nombre de tables ouvertes
    
    dg = dg .. "état taxobox initialisé.<br/>"
    
    -- on parcours les commandes pour trouver les "taxon", afin de valider leur présence
    -- et de trouver le titre de la taxobox
    fnc.tri_taxons()
    if (donnees.defauts.fatal) then -- erreur → fatal
        return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg)
    end
    
    dg = dg .. "taxons analyés (dernier taxon et titre = " .. donnees.defauts.titre_taxobox .. ").<br/>"
    
    -- on traite le titre/sous-titre : est-ce le NS ? Est-ce le NV ? Autre ?
    fnc.genere_titre_taxobox()
    dg = dg .. "titre taxobox géré (titre = " .. donnees.defauts.titre_taxobox .. ").<br/>"

    -- parcours des commandes et appel des fonctions de traitement de chaque ligne
    local tb = ""
    local i = 1
    local courant = ""
    local precedent = ""
    while (donnees.defauts.commandes[i] ~= nil) do
        -- le mot-clé
        local cle = donnees.defauts.commandes[i][1]
        if (cle == nil) then  -- fatal
            fnc.erreur_fatale("La " .. i .. "ème commande n'existe pas.", "interne")
            return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg)
        end
        courant = cle
        dg = dg .. "  - commande : " .. cle .. "<br/>"
        -- on récupère le descriptif de cette commande
        local cmd = fnc.syntaxe[cle]
        if (cmd == nil) then
            dg = dg .. "    -> commande non référencée<br/>"
            fnc.erreur_fatale("La " .. i .. "ème commande (''" .. cle .. "'') n'est pas référencée dans les actions.", "interne")
            return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg)
        end
        dg = dg .. "   -> cmd : " .. type(cmd[1]) .. ", " .. type(cmd[2]) .. ", " .. type(cmd[3]) .. "<br/>"
        -- si pas de fonction associée erreur non fatale et on ignore
        if (cmd[3] == nil) then
        dg = dg .. "    -> commande non implémentée<br/>"
            fnc.erreur_normale("La commande ''" .. cle .. "'' n'est pas encore implémentée.", "interne")
        else
            -- on analyse la ligne
            local etat, resu = pcall(fnc.lecture_parametres, donnees.defauts.commandes[i], 2, cmd[2])
            if (not etat) then
                fnc.erreur_fatale("Échec d'appel à lecture_parametres (" .. cle .. ") (" .. resu .. ").", "interne")
                return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg) -- on quitte
            end
            -- on ajoute le nom de la commande réelle (pour les commandes différentes qui utilisent la même fonction)
            resu["commande"] = cle

            dg = dg .. "    - syntaxe analysée<br/>"
            -- gestion des erreurs
            if (resu == nil or resu.erreurs ~= nil) then
                -- une erreur sur cette ligne est-elle fatale ?
                dg = dg .. "    -> commande retourne une erreur<br/>"
                if (cmd[1] == 2) then
                    fnc.erreur_fatale((resu.erreurs or "non précisé"), "syntaxe")
                    return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg) -- on quitte
                else
                    fnc.erreur_normale((resu.erreurs or "non précisé"), "syntaxe")
                end
            else 
                dg = dg .. "    - syntaxe validée<br/>"
                -- on exécute la ligne
                local tmpr, tmpd
                tmpr, tmpd = pcall(cmd[3], resu)
                if (not tmpr) then
                    fnc.erreur_fatale("Échec d'appel à la fonction de gestion de ''" .. cle .. "'' (" .. tmpd .. ").", "interne")
                    return p.prepare_sortie(fnc.erreur_globale(donnees.defauts, donnees.categorie_erreur), dg) -- on quitte
                end
                dg = dg .. "données : precedent=" .. precedent .. ", courant=" .. courant .. ", tbl=" .. donnees.etat.tbl .. "<br/>"
                -- gestion des fermetures de tables
                if (courant ~= "rang" and courant ~= "conflit" and (precedent == "rang" or precedent == "conflit")) then
                    tb = tb .. "</table>"
                    donnees.etat.tbl = donnees.etat.tbl - 1
                end
                -- cas particulier : on sort de début sans ligne de classification ensuite
                if (donnees.etat.tbl > 0 and courant ~= "rang" and courant ~= "conflit" and precedent == "début") then
                    dg = dg .. "tbl>0 (" .. donnees.etat.tbl .. "), courant ~= rang,conflit (" .. courant .. ")<br/>"
                    -- on quitte un rang → on ferme la table
                    tb = tb .. "</table>"
                    donnees.etat.tbl = donnees.etat.tbl - 1
                end
                -- traitement de "dépannage" : si "rang" sans table ouverte on ouvre la table mais c'est une erreur
                if ((courant == "rang" or courant == "conflit") and donnees.etat.tbl <= 0) then
                    fnc.erreur_normale("Ligne " .. i .. " : commande ''rang'' en dehors d'une zone adéquate.", "syntaxe")
                    tb = tb .. '<table  class="taxobox_classification taxoalternance"><caption>Erreur</caption>\n'
                    donnees.etat.tbl = donnees.etat.tbl + 1
                end

                -- on insert l'élément
                tb = tb .. tmpd
                dg = dg .. "    - commande traitée<br/>"
            end
        end
        -- on bascule courant/precedent
        precedent = courant
        -- ligne suivante
        i = i + 1
    end
    
    -- ici il faut valider que la taxobox est terminée proprement
    if (donnees.etat.tbl >= 1) then
        -- on ferme les tables
        for i = 1, donnees.etat.tbl do
            tb = tb .. "</table>"
        end
        donnees.etat.tbl = 0
    end
    -- fermeture
    if (donnees.etat.ferme == false) then
        tb = tb .. "</div>"
        donnees.etat.ferme = true
    end
    
    -- traitement titre article (mise en forme si nécessaire)
    local mtitre = ""
    if (donnees.defauts.taxon_titre ~= nil) then -- il y a un titre NS
        if (donnees.defauts.c_titre ~= "non") then
            dg =dg .. "titre a modifier a priori : titre = " .. donnees.defauts.c_force_titre .. ", ciblé = " .. donnees.defauts.commandes[donnees.defauts.taxon_titre][3] .. "<br/>"
            -- le titre modifié
            local tmp = fnc.italiques_titre(donnees.defauts.c_force_titre, donnees.defauts.commandes[donnees.defauts.taxon_titre][3], donnees.defauts.commandes[donnees.defauts.taxon_titre][2])
            dg = dg .. " -> " .. tmp .. "<br/>"
            -- selon le mode
            if (donnees.defauts.c_titre == "boîte" or (donnees.defauts.c_titre == "auto" and not donnees.defauts.article)) then
                mtitre = mtitre .. fnc.box_ouverture("Titre")
                mtitre = mtitre .. fnc.box_texte(frame:preprocess("<nowiki>" .. tmp .. "</nowiki>"))
                mtitre = mtitre .. fnc.box_fermeture()
            elseif (donnees.defauts.c_titre == "oui" or (donnees.defauts.c_titre == "auto" and donnees.defauts.article)) then
                mtitre = mtitre .. frame:preprocess(tmp)
            end
        end
    end

    -- traitement sous-titre éventuel
    local mstitre = ""
    if (donnees.defauts.c_sous_titre ~= "non") then
        dg = dg .. "sous-titre potentiel<br/>"
        local sst = nil
        -- si taxon_titre → titre en NS, on ajoute le NV si présent
        if (donnees.defauts.taxon_titre ~= nil) then
            dg = dg .. " -> vernaculaire : " .. (donnees.defauts.nom_vernaculaire or "non défini") .. "<br/>"
            sst = donnees.defauts.nom_vernaculaire
        else
            -- titre NV, on ajoute le NS en sous-titre
            dg = dg .. " -> scientifique : " .. donnees.defauts.titre_taxobox .. "<br/>"
            sst = donnees.defauts.titre_taxobox
        end
        if (donnees.defauts.c_sous_titre == "boîte" or (donnees.defauts.c_sous_titre == "auto" and not donnees.defauts.article)) then
            if (sst ~= nil) then
                mstitre = mstitre .. fnc.box_ouverture("Sous-titre")
                mstitre = mstitre .. fnc.box_texte(sst)
                mstitre = mstitre .. fnc.box_fermeture()
            end
        elseif (donnees.defauts.c_sous_titre == "oui" or (donnees.defauts.c_sous_titre == "auto" and donnees.defauts.article)) then
            -- on l'insert en tant que sous-titre
            if (sst ~= nil) then
                mstitre = mstitre .. '<span id="sous_titre_h1">' .. sst .. '</span>'
            end
        end
    end
    
    -- si besoin et selon les modalités on insert les catégories
    local mcat = ""
    if (donnees.defauts.r_categories[1] ~= nil and donnees.defauts.c_categories ~= "non") then
        -- selon le mode
        if (donnees.defauts.c_categories == "boîte" or (donnees.defauts.c_categories == "auto" and donnees.defauts.article == false)) then
            mcat = mcat .. fnc.box_ouverture("Catégorie(s)")
            mcat = mcat .. fnc.box_texte(donnees.defauts.r_categories, true)
            mcat = mcat .. fnc.box_fermeture()
        elseif (donnees.defauts.c_categories == "en ligne" or (donnees.defauts.c_categories == "auto" and donnees.defauts.article == true)) then
            -- insertion "en vrai"
            local i = 1
            while (donnees.defauts.r_categories[i] ~= nil) do
                mcat = mcat .. "[[Catégorie:" .. donnees.defauts.r_categories[i] .. "]]"
                i = i + 1
            end
        end
    end
    
    -- si besoin et selon les modalités on insert les catégories d'erreur
    local mcaterr = ""
    if (donnees.defauts.r_err_categories[1] ~= nil and donnees.defauts.c_err_categories ~= "non") then
        dg = dg .. "Il y a des catégories d'erreur<br/>"
        -- selon le mode
        if (donnees.defauts.c_err_categories == "boîte" or (donnees.defauts.c_err_categories == "auto" and donnees.defauts.article == false)) then
            mcaterr = mcaterr .. fnc.box_ouverture("Catégorie d'erreur(s)")
            mcaterr = mcaterr .. fnc.box_texte(donnees.defauts.r_err_categories, true)
            mcaterr = mcaterr .. fnc.box_fermeture()
        elseif (donnees.defauts.c_err_categories == "en ligne" or (donnees.defauts.c_err_categories == "auto" and donnees.defauts.article == true)) then
            -- insertion "en vrai"
            local i = 1
            while (donnees.defauts.r_err_categories[i] ~= nil) do
                mcat = mcat .. "[[Catégorie:" .. donnees.categorie_erreur .. "|" .. donnees.defauts.r_categories[i] .. "]]"
                i = i + 1
            end
        end
    end
    
    -- si besoin et selon les modalités on insert les erreurs
    local merr = ""
    if (donnees.defauts.r_erreurs[1] ~= nil and donnees.defauts.c_erreurs ~= "non") then
        -- selon le mode
        if (donnees.defauts.c_erreurs == "boîte" or (donnees.defauts.c_erreurs == "auto" and donnees.defauts.article == false)) then
            merr = merr .. fnc.box_ouverture("Erreur(s)")
            merr = merr .. fnc.box_texte(donnees.defauts.r_erreurs)
            merr = merr .. fnc.box_fermeture()
        end
    end

    -- terminé, on retourne le tout
    return p.prepare_sortie(tb .. mtitre .. mstitre .. mcat .. mcaterr .. merr, dg)
end


-- cherche le | suivant, en ignorant ceux dans des modèles ou wikiliens
-- retourne une position (utf8)
function p.pipe_suivant(ligne, posr)
    local pos = posr
    local cnt = 0
    while (true) do
        local c = mw.ustring.sub(ligne, pos, pos)
        if (c == nil or c == "") then
            if (pos <= posr) then
                return nil
            else
                return pos
            end
        end
        -- on comptabilise les entrées/sorties de modèle/lien
        if (c == "[" or c == "{") then
            cnt = cnt + 1
        elseif (c == "]" or c == "}") then
            cnt = cnt - 1
        elseif (c == "|") then
            -- si | on retourne la position que si cnt = 0
            if (cnt == 0) then
                return pos
            end
        end
        pos = pos + 1
    end
    -- on ne vient pas là
    return nil
end

-- découpe selon les | et range dans une table
function p.decoupe_pipe(ligne)
    local tbl = {}
    local pos
    local courant = 1

    pos = z.pipe_suivant(ligne, 1)
    if (pos == nil) then
        table.insert(tbl, ligne) -- un seul élément
        return tbl
    end

    while (pos ~= nil) do
        -- on recupere de "courant" à pos
        local tmp = mw.ustring.sub(ligne, courant, pos-1)
        table.insert(tbl, tmp)
        courant = pos + 1

        -- on recupere la partie suivante
        pos = p.pipe_suivant(ligne, courant)
    end

    return tbl
end


--[[
  Découpe un modèle en ses paramètres
--]]
function p.decoupe_modele(modele)
    
    
end


--[[
  Prend en paramètre un modèle modulaire, et retourne sa version monolytique
--]]
function p.mono_vers_modu(modele)
    
    return ""
end






-- on retourne le module
return p