Vejatz lo contengut

Mòdul:Taxobox

Un article de Wikipèdia, l'enciclopèdia liura.

La documentacion d'utilizacion d'aquel modul se pòt crear a Mòdul:Taxobox/ús

-- module principal (de test) pour générer des taxobox.
-- contient la fonction principale (et unique) "taxobox"
-- l'autre fonction (convertisseur) n'est pas fonctionnelle.
-- il reste des choses à faire, nettoyer…

local z = {}

-- les boîtes à outils
local data = require "Module:Taxobox-data"
local briques = require "Module:Taxobox-teules"
local tools = require "Module:Taxobox-aisinas"
 
 
-- preparation : table "globale" contenant les divers paramètres communs
--   contient aussi les tables et éléments pour les retours d'info des fonctions
z.configuration = {}
-- la "frame", nécessaire pour faire du preprocess si besoin
z.configuration.frame = nil  -- fram
-- paramètres qui sont des données
z.configuration.regne = nil  -- le règne
z.configuration.nv = nil     -- le(s) nom(s) vernaculaire(s) si présent
 
-- informations sur l'article
z.configuration.titre_page = nil  -- le titre réel de l'article
z.configuration.article = nil  -- le namespace de la page (true=article, false=autre)
-- titre forcé : faire comme si c'était le titre de l'article (implique NS=0)
z.configuration.force_titre = nil
 
-- informations sur la taxobox (sa structure)
z.configuration.div_nb = 0  -- compte des ouvertures / fermertures de <div>
z.configuration.table_nb = 0  -- compte des ouvertures / fermetures de <table>
z.configuration.fermeture_taxobox = nil  -- état de la taxobox (redondant avec div_nb ?)
 
-- informations calculées
z.configuration.titre_article = nil  -- titre mis en forme de l'article (pour modification affichage)
z.configuration.titre_taxobox = nil  -- titre de la taxobox (mis en forme) calculé d'après les infos
z.configuration.titre_ns = nil  -- si true → article titré avec le (un) nom scientifique, sinon nom vernaculaire
z.configuration.regne_affiche = nil  -- le nom wikifié correspondant au règne
z.configuration.regne_lien = nil  -- le lien vers lequel pointe ce règne
z.configuration.sous_titre = ""  -- le sostitre (éventuel) à insérer
 
-- zones de stockage d'information pour les retours
z.configuration.r_categories = {}  -- stockage des catégories retournées par les fonctions
z.configuration.r_err_categories = {} -- stockage des catégories d'erreur retournées par les fonctions
z.configuration.r_erreurs = {}  -- stockage des erreurs retournées par les fonctions
 
-- fonctions d'accès pour remplir les informations (cat, err-cat, err)
z.configuration.f_categories = nil  -- fonction d'ajout d'une catégorie
z.configuration.f_err_categories = nil  -- fonction d'ajout d'une catégorie d'erreur
z.configuration.f_erreurs = nil  -- fonction d'ajout d'une erreur { type, message }
 
-- les éléments de contrôle de la sortie
z.configuration.c_titre = nil  -- la gestion du titre (oui, non, auto)
z.configuration.c_sous_titre = nil  -- la gestion du sostitre (oui, non, auto)
z.configuration.c_categories = nil -- la gestion des catégories (en ligne, boîte, non, auto)
z.configuration.c_err_categories = nil -- la gestion des catégories d'erreurs (en ligne, boîte, non, auto)
z.configuration.c_erreurs = nil -- la gestion des messages d'erreur (oui, non, auto)
 
-- les éléments réservés aux développeurs
z.configuration.c_raw = nil  -- affichage "brut" de la taxobox
z.configuration.c_debug = nil  -- mode debug. Ne fait rien actuellement
 
 
-- l'ensemble des paramètres non-nommés, sous forme de table de tables (1 par ligne)
local descriptions = {}
local nb_lignes -- nombre de lignes dans la description
 
-- variables d'état de la taxobox : contient les états en cours
local etat_precedent = ""  -- ce qu'on a traité juste avant
local liste_taxons = {}  -- liste des taxons trouvés
local nb_taxons  -- nombre de lignes dans liste_taxons
local cible_taxon = nil  -- si présent, l'utiliser plutôt que nb_taxons pour choix du titre
local nb_debut = 0  -- pour gérer les ouvertures / fermetures
local nb_cites = 0  -- pour gérer la proximité
local nb_uicn = 0  -- pour gérer la proximité
local pdebug = ""  -- pour le mode debug
 
 
-- fonctions de manipulation des infos
function z.ajoute_r_categories(nom)
    if (nom == nil) then
        return
    end
    table.insert(z.configuration.r_categories, nom)
end
function z.ajoute_r_err_categories(nom)
    if (nom == nil) then
        return
    end
    table.insert(z.configuration.r_err_categories, nom)
end
function z.ajoute_r_erreurs(tp, desc)
    if (tp ~= nil and tp ~= "" and desc ~= nil and desc ~= "") then
        local ll = ""
        if (type(z.configuration.ligne_courante) == "string") then
            ll = z.configuration.ligne_courante
        elseif (type(z.configuration.ligne_courante) == "table") then
            ll = table.concat(z.configuration.ligne_courante, "|")
        else
            ll = "- pas d'information -"
        end
        table.insert(z.configuration.r_erreurs, {tp,desc .. "<br/>(" .. ll .. ")" })
    end
end
-- initialisation des fonctions de traitement de catégorie
z.configuration.f_categories = z.ajoute_r_categories
z.configuration.f_err_categories = z.ajoute_r_err_categories
z.configuration.f_erreurs = z.ajoute_r_erreurs
 
 
 
-- cette fonction nettoie les blancs au début et à la fin du texte. Utile pour nettoyer
-- les paramètres reçus qui peuvent contenir des blancs selon la mise en forme à l'appel 
function z.nettoie(nom)
    -- histoire de ne pas générer d'erreur
    if (nil == nom) then
        return nil
    end
    local tmp = string.gsub (nom, "^[%s]*", "")
    return string.gsub (tmp, "[%s]*$", "")
end
 
 
-- indique si un mot est dans une liste. Si oui retourne la position dans la liste
function z.est_dans_liste(mot, liste)
    local i = 1
 
    if (liste == nil) then
        return 0
    end
    while (liste[i] ~= nil) do
        if (liste[i] == mot) then
            return i
        end
        i = i + 1
    end
    return 0
end
 
 
-- lecture d'une ligne de paramètres
-- desc est la description symbolique de ce qu'on attend
-- ligne est une table (de string) contenant la ligne découpée
-- depart est l'offset dans la table où commencer
-- Retourne une table où se trouvent fixées les valeurs trouvées
-- Format de la description :
-- {
--   ["noname"] = { "key1", "key2", ... },
--   ["flags"] = { { { "f1", "f1b", ... }, "key" },   { { "f2", "f2b", ... }, "key2" }, ... },
--   |"values"] = { { { "v1", "v1b", ... }, "key" },   { { "v2", "v2b", ... }, "key2" }, ... }
-- }
-- noname : paramètres sans nom sur la ligne. La fonction associe tout paramètre qui n'est ni un
--   flag ni un value à la liste de mots-clés. Dans la table retournée on aura tab["key1"] = "le premier champs non nommé",
--   tab["key2" = "le 2ème champs non nommé"... Ceux non présents valent nil. Si trop de paramètres non nommés sont présents
--   les derniers sont ignorés et une erreur est retournée (voir la gestion des erreurs plus bas)
-- flags : paramètres ayant un nom et qui ne sont gérés que par présent/absent. La fonction compare chaque paramètre à la liste
--   des mots-clés possibles pour chaque flag (f1, f1b pour le premier, f2, f2b…). Si l'un est présent la fonction fixe
--   tab["key"] = true (nil sinon)
--   si un flag est donné plus d'une fois cela génère une erreur
-- values : identique aux flags, mais la fonction cherche une valeur associée, qui est le paramètre suivant. Cette valeur
--   est passée par tab["key"] = "valeur lue".
--   Si une value n'a pas de valeur (dernier élément de la liste) ou est donnée plusieurs fois cela génère une erreur
-- Erreurs : si tab["erreurs"] est non nil / non vide c'est qu'une erreur s'est produite. tab["erreurs"] contient un message
--   expliquant l'erreur
-----
----- TODO : remplacer z.ajoute_erreur ici pour transmettre l'erreur à celui qui appelle.
-----
function z.lecture_parametres(ligne, depart, desc)
    local i = depart
    local buf = ""
    local res = {}
    local j
    local tmpf
 
    -- on parcours les entrées
    while (ligne[i] ~= nil) do
        v = z.nettoie(ligne[i])
 
        --- on cherche si 'v' correspond à un élément des descriptions
        j = 1
        if (desc["flags"] == nil) then
            tmpf = 0 -- pas de flags
        else
            while (desc["flags"][j] ~= nil) do
                tmpf = z.est_dans_liste(v, desc["flags"][j][1])
                if (tmpf > 0) then
                    break
                end
                j = j + 1
            end
        end
        if (tmpf > 0) then
            -- on a trouvé, c'est un flag
            -- on vérifie qu'il n'est pas déjà donné
            if (res[desc["flags"][j][2]] ~= nil) then
                res["erreurs"] = "Element ''" .. ligne[i] .. "'' en exemplars multiples"
            end
            -- quoi qu'il en soit c'est le dernier qui gagne
            res[desc["flags"][j][2]] = true
        else
            -- pas un flag, on regarde les "values"
            j = 1
            if (desc["values"] == nil) then
                tmpf = 0
            else
                while (desc["values"][j] ~= nil) do
                    tmpf = z.est_dans_liste(v, desc["values"][j][1])
                    if (tmpf > 0) then
                        break
                    end
                    j = j + 1
                end
            end
            if (tmpf > 0) then
                -- on a trouvé, c'est un values
                -- on vérifie qu'il y a un élément en plus
                if (ligne[i+1] == nil) then
                    -- c'est une erreur
                    res.erreurs = "Valor ''" .. v .. "'' sens contengut (casa seguenta)."
                else
                    -- on vérifie qu'il n'est pas déjà donné
                    if (res[desc["values"][j][2]] ~= nil) then
                        res.erreurs = "Element ''" .. ligne[j] .. "'' en exemplars multiples."
                    end
                    -- quoi qu'il en soit c'est le dernier qui gagne
                    res[desc["values"][j][2]] = z.nettoie(ligne[i+1])
                    -- on saute l'élément
                    i = i + 1
                end
            else
                -- c'est un paramètre non nommé
                -- on cherche le premier non nommé qui soit nil
                j = 1
                while (desc["noname"][j] ~= nil) do
                    if (res[desc["noname"][j]] == nil) then
                        break
                    end
                    j = j + 1
                end
                if (desc["noname"][j] == nil) then
                    -- donc il y a trop de paramètres -> erreur
                    res.erreurs = "Tròp de paramètres"
                else
                    -- on fixe sa valeur
                    res[desc["noname"][j]] = v
                end
            end
        end
        -- entrée suivante
        i = i + 1
    end
    return res
end
 
 
 
 
 
--- fonctions de traitement des lignes de description
-- wrapper effectuant la lecture des paramètres et traitant les erreurs
function z.traite_avec_erreur(ligne, syntaxe)
 
    local params = z.lecture_parametres(ligne, 2, syntaxe)
    if (params.erreurs ~= nil) then
        return nil, "sintaxi", params.erreurs
    end
    return params, nil, nil
end
 
 
 
-- devrait disparaitre aussi : il faut revoir l'appel explicite dans la fonction principale
 
-- fin de la taxobox. Syntaxe :
-- fin
function z.traite_fin(ligne)
 
    -- si la taxobox n'est pas ouverte
    if (fermeture_taxobox == true) then
        z.ajoute_r_erreurs("sintaxi", "Tampadura de taxobox sens que siá dobèrta.")
        z.ajoute_r_err_categories("Taxobox amb error/sintaxi")
        return ""
    end
 
    -- on indique que la taxobox est fermée
    fermeture_taxobox = true
    local tmp = tools.t_vraie_fin(z.configuration)
 
    return tmp
end
 
 
-- prend une ligne en paramètre et appelle la bonne fonction, et gère les erreurs
function z.traite_ligne(ligne)
    local prems
    local res = ""
    local courant
 
    if (ligne == nil) then
        z.ajoute_r_erreurs("interne (traite_ligne)", "La linha correnta val ''nil''.")
        z.ajoute_r_err_categories("Taxobox amb error/intèrne")
        return nil
    end
    prems = ligne[1]
    if (prems == nil) then
        z.ajoute_r_erreurs("interne (traite_ligne)", "Pas de primièr element dins la linha recebuda.")
        z.ajoute_r_err_categories("Taxobox amb error/intèrne")
        return nil
    end
 
    -- on récupère la ligne décrivant cet élément
    local desc = tools.d_syntaxe[prems]
    if (desc == nil) then
        z.ajoute_r_erreurs("sintaxi", "Lo mot clau " .. prems .. " es desconegut.")
        z.ajoute_r_err_categories("Taxobox amb error/sintaxi")
        return nil
    end
    -- desc contient dans l'ordre le niveau de criticité de la ligne (1/2/3),
    --  la description du format de la ligne, la fonction à appeler
 
    -- on évalue la ligne
    local vals, stl, msg = z.traite_avec_erreur(ligne, desc[2])
    -- erreur de syntaxe ?
    if (vals == nil) then
        -- on retourne une erreur, fatale si demandé
        z.ajoute_r_erreurs(stl, msg)
        z.ajoute_r_err_categories("Taxobox amb error/" .. stl)
        if (desc[1] == 2) then
            return nil -- erreur fatale
        else
            return "" -- ligne ignorée
        end
    end
 
    -- on appelle la fonction de traitement
    courant = ligne[1] -- on note l'élément en cours de traitement
    z.configuration.ligne_courante = ligne -- pour les erreurs
    local reterr, tmp = pcall( desc[3], vals, z.configuration )
    if (reterr ~= true) then -- erreur
        if (desc[1] == 2) then
            -- pas de gestion des messages d'erreur+cat : c'est fait dans les fonctions
            return nil -- erreur fatale
        else
            -- pas de gestion des messages d'erreur+cat : c'est fait dans les fonctions
            return "" -- ligne ignorée
        end
    end
    -- si tmp == nil et qu'on est là c'est qu'on ignore la ligne :
    if (tmp == nil) then
        tmp = ""
    end
 
    --- ici on insert les tests de cohérence sur la structure des éléments
 
    -- début doit être le premier. Il ne peut y en avoir qu'un
    if (courant == "començament" and nb_debut > 0) then
        z.ajoute_r_erreurs("estructura", "Mai de un ''començament'' donat.")
        z.ajoute_r_err_categories("Taxobox amb error/estructura")
        return nil -- c'est fatal
    end
    if (courant ~= "començament" and nb_debut == 0) then
        z.ajoute_r_erreurs("estructura", "Element ''" .. courant .. "'' abans un ''començament''.")
        z.ajoute_r_err_categories("Taxobox amb error/estructura")
        return nil -- c'est fatal
    end
    if (courant == "començament") then
        nb_debut = nb_debut + 1
    end
 
    -- si on sort d'un "rang" on doit fermer la table
    if ( (etat_precedent == "reng" or etat_precedent == "conflicte") and (courant ~= "reng" and courant ~= "conflicte") ) then
        res = res .. "</table>\n"
        z.configuration.table_nb = z.configuration.table_nb - 1
    end
 
    -- si on rentre dans un "rang" il faut qu'on soit dans une table.
    -- ici on accepte mais on génère une erreur
    if ( (courant == "reng" or courant == "conflicte") and z.configuration.table_nb == 0) then
        z.ajoute_r_erreurs("estructura", "Element ''" .. courant .. "'' en defòra d'una zòna de classificacion.")
        z.ajoute_r_err_categories("Taxobox amb error/estructura")
        -- on ouvre une table (pas propre) pour traiter
        res = res .. "<table>"
    end    
 
    -- rang/conflit : un rang ne peut apparaître que :
    --  - après un autre rang (on est dans la classification)
    --  - après "début" (sauf si phylo est indiqué)
    --  - après "bandeau phylo" (non implémenté encore)
    -- dans les autres cas : erreur, ignore ou corrige (<table[/]>) selon option
 
    -- légende/reparticion légende : une légende ne peut que suivre une légende ou une image
 
    -- taxon : les taxons devraient être donnés à la suite, pas séparés
    if (courant == "taxon" and (etat_precedent ~= "taxon" and etat_precedent ~= "reng" and etat_precedent ~= "conflicte") ) then
        z.ajoute_r_erreurs("estructura", "Element ''taxon'' a una posicion incorrècta.")
        z.ajoute_r_err_categories("Taxobox amb error/estructura")
    end -- mais ce n'est pas fatal
 
    -- UICN / CITES : devraient être groupés s'il y en a plusieurs ?
    if (courant == "cites") then
        if (nb_cites > 0 and etat_precedent ~= "cites") then
            z.ajoute_r_erreurs("estructura", "Element ''CITES'' multiples mas pas consecutius.")
            z.ajoute_r_err_categories("Taxobox amb error/estructura")
        end
        nb_cites = nb_cites + 1
    end
    if (courant == "uicn") then
        if (nb_uicn > 0 and etat_precedent ~= "uicn") then
            z.ajoute_r_erreurs("estructura", "Element ''UICN'' multiples mas pas consecutius.")
            z.ajoute_r_err_categories("Taxobox amb error/estructura")
        end
        nb_uicn = nb_uicn + 1
    end
 
 
    -- synonymes / taxons : devrait être au plus près du dernier taxon
 
    -- ...
 
 
    -- on ajoute l'élément
    res = res .. tmp
 
    -- bascule courant / précédent
    if (courant ~= nil) then  -- on ne touche pas si erreur car on n'a rien fait
        etat_precedent = courant
    end
 
    -- on retourne le résultat
    return res
end
 
 
 
-- cette fonction traite tous les paramètres non-nommés pour créer la table globale associée
-- retourne le nombre total
function z.traite_parametres(pframe)
    local k, v
    local ligne = {}
    local pos = 1
    local pd = 1
 
    for k,v in pframe:argumentPairs() do
        if (type(k) == "number") then
            local temp = string.sub(v, -1)
 
            local txt = z.nettoie(v)
            if (pos == 1) then
                -- le premier est passé en minuscules
                if (txt ~= nil) then
                    ligne[pos] = string.lower(txt)
                else
                    ligne[pos] = txt
                end
            else
                ligne[pos] = txt
            end
            pos = pos + 1
            if (temp == "\n") then
                descriptions[pd] = ligne
                pd = pd + 1
                ligne = {}
                pos = 1
            end
        end
    end
    return pd - 1
end
 
-- retourne vrai si le mot-clé "titre" ou "en titre" est présent
-- dans la table, sinon faux (ainsi qu'en cas d'erreur)
function z.presence_titre(lst)
    if (lst == nil or type(lst) ~= "table") then
        return false
    end
    local i = 1
    while (lst[i] ~= nil) do
        if (type(lst[i]) == "string") then
            if (lst[i] == "títol" or lst[i] == "en títol") then
                return true
            end
        end
        i = i + 1
    end
    return false
end
 
 
-- cette fonction cherche toutes les occurrences de "taxon" pour déterminer s'il y en a et combien
-- génère la liste des taxons qui est une table { rang, nom } et retourne le nombre trouvé
-- ils sont classés dans l'ordre
-- si une ligne taxon est invalide elle est ignorée (ici)
-- si le mot-clé "titre" ou "en titre" est présent le 3ème champs est mis à vrai
function z.cherche_taxons()
    local nb = 0
    local pos = 1
 
    -- tant qu'il y a des entrées
    while (descriptions[pos] ~= nil) do
        -- si c'est "taxon"
        if (descriptions[pos][1] == "taxon") then
            local rang = descriptions[pos][2]
            local nom = descriptions[pos][3]
            --  gestion des erreurs
            if (rang ~= nil and nom ~= nil) then
                -- présence de "titre" ?
                local ttl = false
                if (z.presence_titre(descriptions[pos])) then
                    ttl = true
                    cible_taxon = nb + 1
                end
                -- on l'ajoute
                nb = nb + 1
                liste_taxons[nb] = { rang, nom, ttl }
            end
        end
        pos = pos + 1
    end
    return nb
end
 
 
 
-- utilise liste_taxons pour trouver les titres
function z.cherche_titres(frame)
    local pos = 1
 
    -- en premier lieu on cherche le taxon le plus "bas" pour le titre de la taxobox
    local ligne
    if (cible_taxon ~= nil) then
        ligne = liste_taxons[cible_taxon]
        if (ligne == nil) then
            -- protection
            ligne = liste_taxons[nb_taxons]
            z.ajoute_r_erreurs("intèrne/cibla_taxon", "cibla_taxon=" .. cible_taxon .. " mentre que nb_taxons=" .. nb_taxons .. ", retorn a ''nil''.")
            z.ajoute_r_err_categories("Taxobox amb error/intèrne")
        end
    else
        ligne = liste_taxons[nb_taxons]
    end
    z.configuration.titre_taxobox = tools.formate_ns(z.configuration.regne, ligne[1], ligne[2], false, false)
 
    -- on découpe le titre de l'article pour extraire la partie homonymie
    local tmp = tools.decoupe_homonymie(z.configuration.titre_page)
    -- on cherche dans la liste des taxons si l'un d'entre-eux correspond au nom (sans partie homonynie)
    local trouve = 0
    for i = 1, nb_taxons do
        if (tmp[1] == liste_taxons[i][2]) then
            trouve = i
            -- si celui qui correspond au titre n'est pas le dernier on insert une erreur+cat
            -- sauf si il est explicitement indiqué comme titre
            if (i ~= nb_taxons) then
                if (liste_taxons[i][3] == false) then
                    z.ajoute_r_erreurs("estructura/taxons", "Taxobox gigònha mas article pas titolat amb lo taxon lo mai bas")
                    z.ajoute_r_err_categories("Taxobox amb error/estructura")
                end
            end
            break
        end
    end
 
    -- si pas trouvé : probablement titré en nom vernaculaire
    if (trouve == 0) then
        -- on ne touche pas au titre
        z.configuration.titre_article = nil
        -- on met en sostitre la liste des taxons
        for i = 1, nb_taxons do
            -- on met en forme le nom scientifique
            local ttr = tools.formate_ns(z.configuration.regne, liste_taxons[i][1], liste_taxons[i][2], false, false)
            if (i < nb_taxons) then
                z.configuration.sous_titre = z.configuration.sous_titre .. ttr .. ", "
            else
                z.configuration.sous_titre = z.configuration.sous_titre .. ttr
            end
        end
    else
        -- la mise en forme du titre
        z.configuration.titre_article = tools.formate_ns(z.configuration.regne, liste_taxons[trouve][1], tmp[1], true, false)
        if (tmp[2] ~= nil) then
            z.configuration.titre_article = z.configuration.titre_article .. " " .. tmp[2]
        end
        -- puisque NS on ajoute les NV en sostitre si présents
        if (z.configuration.nv == nil or z.configuration.nv == "") then
            z.configuration.sous_titre = nil
        else
            z.configuration.sous_titre = z.configuration.nv
        end
    end
 
end
 
function z.essai(frame)
    tools.t_test(nil, z.configuration)
 
    if (z.configuration.r_erreurs ~= nil) then
        return z.configuration.r_erreurs[1][1] .. " : " .. z.configuration.r_erreurs[1][2] .. "<br/>"
    else
        return "Pas cap de retorn.<br/>"
    end
 
 
end
 
-- génère une box lisant les erreurs (selon le mode)
function z.box_erreurs(mode)
    local tx = ""
    local pos = 1
 
    if (mode == "non" or z.configuration.r_erreurs == nil or z.configuration.r_erreurs[1] == nil) then
        return "" -- rien (rien à faire ou mode "pas d'erreurs")
    end
 
    -- si mode auto et article → on ne fait rien non plus
    if (mode == "auto" and z.configuration.article == true) then
        return ""
    end
 
    -- donc là on affiche (soit mode="òc" soit mode="auto" + article)
    tx = tx .. briques.t_debut("error", "Errors detectadas")
    while (z.configuration.r_erreurs[pos] ~= nil) do
        tx = tx .. "<small>" .. z.configuration.r_erreurs[pos][1] .." : " .. z.configuration.r_erreurs[pos][2] .. "</small>"
        pos = pos + 1
    end
 
    tx = tx .. briques.t_end()
 
    return tx
end
 
 
-- "pousse" la table de catégories selon le bon mode
function z.mef_categories(liste, mode, msg1, msg2)
    local ret = ""
 
    -- vide, ou bien mode = "non"
    if (mode == "non" or liste == nil or liste[1] == nil) then
        return ""
    end
    local t -- 1=inline, 2=box
 
    if (mode == "en linha") then
        t = 1
    elseif (mode == "bóstia") then
        t = 2
    else -- si mode "auto" on regarde dans quel espace on est
        if (z.configuration.article == true) then
            t = 1 -- dans les articles mode inline
        else
            t = 2 -- sinon mode box
        end
    end
 
    -- mode boite : entête
    if (t == 2) then
        ret = ret .. briques.t_debut(msg1, msg2)
    end
 
    local pos = 1
    while (liste[pos] ~= nil) do -- on parcours
        local p1 = nil  -- catégorie
        local p2 = nil  -- clé éventuelle
        if (type(liste[pos]) == "string") then
            p1 = liste[pos]
            p2 = nil
        elseif (type(liste[pos]) == "table") then
            p1 = liste[pos][1] or "Taxobox amb error/intèrne"
            p2 = liste[pos][2]
        else
            p1 = "Taxobox amb error/intèrne"
            p2 = nil
        end
        if (t == 1) then
            if (p2 == nil) then
                ret = ret .. "[[Categoria:" .. p1 .. "]]"
            else
                ret = ret .. "[[Categoria:" .. p1 .. "|" .. p2 .. "]]"
            end
        else
            if (p2 == nil) then
                ret = ret .. "[[:Categoria:" .. p1 .. "|" .. p1 .. "]]<br/>"
            else
                ret = ret .. "[[:Categoria:" .. p1 .. "|" .. p1 .. " (" .. p2 .. ")]]<br/>"
            end
        end
        pos = pos + 1
    end
 
    -- mode boite : la fin
    if (t == 2) then
        ret = ret .. briques.t_end()
    end
 
    return ret
end
 
 
-- génère une box listant les catégories ou les incluant (selon le mode)
function z.box_categories()
    local tx = ""
    local pos = 1
 
    -- categories "normales"
    tx = tx .. z.mef_categories(z.configuration.r_categories, z.configuration.c_categories, "", "Categorias pas inclusas")
 
    -- catégories d'erreur
    tx = tx .. z.mef_categories(z.configuration.r_err_categories, z.configuration.c_err_categories, "error", "Categorias d'error pas inclusas")
 
    return tx
end
 
 
-- fonction principale
function z.taxobox(frame)
    local pframe = frame:getParent()
    local args = pframe.args
    local tx = ""
    local tmp
 
    -- on conserve la frame
    z.configuration.frame = frame

    -- on récupère le NAMESPACE tout de suite, pour que les briques erreur puissent catégoriser correctement
    z.configuration.article = frame:preprocess("{{NAMESPACE}}")  -- le namespace de la page
    if (z.configuration.article == "") then
        -- c'est un article
        z.configuration.article = true
    else
        z.configuration.article = false
    end

    -- -- gestion des paramètres nommés (debug, options, éléments obligatoires)
    -- - paramètres éditoriaux
    -- on récupère le règne
    z.configuration.regne = z.nettoie(args["règne"]) or nil
    -- pas de règne, erreur tout de suite
    if (z.configuration.regne == nil) then
        return briques.t_erreur("Lo paramètre nomenat ''règne'' es obligatòri.", "règne", z.configuration)
    end
    -- on recupère les noms vernaculaires
    z.configuration.nv = z.nettoie(args["nv"] or args["nom vernacular"] or args["noms vernaculars"]) or nil
 
    -- - paramètres modifiant le comportement (note : passer ça en minuscules)
    -- titre : oui, non, auto → contrôle la modification (éventuelle) du titre
    z.configuration.c_titre = z.nettoie(args["títol"]) or nil
    if (z.configuration.c_titre == nil) then
        z.configuration.c_titre = data.defauts.titre -- mode par défaut
    end
    -- validation du paramètre
    if (z.configuration.c_titre == "bóstia") then
        z.configuration.c_titre = "bóstia"
    end
    if (z.configuration.c_titre ~= "òc" and z.configuration.c_titre ~= "non" and z.configuration.c_titre ~= "bòstia" and z.configuration.c_titre ~= "auto") then
        return briques.t_erreur("Lo paramètre nomenat ''títol'' pòt pas prene que las valors ''òc'', ''non'' o ''auto''.", "títol", z.configuration)
    end
    -- sostitre : oui, non, auto → contrôle la modification (éventuelle) du sostitre
    z.configuration.c_sous_titre = z.nettoie(args["sostítol"]) or nil
    if (z.configuration.c_sous_titre == nil) then
        z.configuration.c_sous_titre = data.defauts.sous_titre -- mode par défaut
    end
    -- validation du paramètre
    if (z.configuration.c_sous_titre == "bóstia") then
        z.configuration.c_sous_titre = "bóstia"
    end
    if (z.configuration.c_sous_titre ~= "òc" and z.configuration.c_sous_titre ~= "non"  and z.configuration.c_sous_titre ~= "bóstia" and z.configuration.c_sous_titre ~= "auto") then
        return briques.t_erreur("Lo paramètre nomenat ''sostítol'' pòt pas prene que las valors ''òc'', ''non'' o ''auto''.", "sostítol", z.configuration)
    end
    -- erreurs : oui, non, auto → contrôle l'insertion des messages d'erreur
    z.configuration.c_erreurs = z.nettoie(args["errors"]) or nil
    if (z.configuration.c_erreurs == nil) then
        z.configuration.c_erreurs = data.defauts.erreurs -- mode par défaut
    end
    -- validation du paramètre
    if (z.configuration.c_erreurs ~= "òc" and z.configuration.c_erreurs ~= "non" and z.configuration.c_erreurs ~= "auto") then
        return briques.t_erreur("Lo paramètre nomenat ''errors'' pòt pas prene que las valors ''òc'', ''non'' o ''auto''.", "errors", z.configuration)
    end
    -- catégories : en ligne, boite, non, auto → contrôle l'insertion des catégories "normales"
    z.configuration.c_categories = z.nettoie(args["categorias"] or args["categoria"] or args["categories"] or args["categorie"]) or nil
    if (z.configuration.c_categories == nil) then
        z.configuration.c_categories = data.defauts.categories -- mode par défaut
    end
    -- validation du paramètre
    if (z.configuration.c_categories == "bóstia") then
        z.configuration.c_categories = "bóstia"
    end
    if (z.configuration.c_categories ~= "en linha" and z.configuration.c_categories ~= "bóstia" and z.configuration.c_categories ~= "non" and z.configuration.c_categories ~= "auto") then
        return briques.t_erreur("Lo paramètre nomenat ''categorias'' pòt pas prene que las valors ''en linha'', ''bóstia'', ''non'' o ''auto''.", "categorias", z.configuration)
    end
    -- catégories d'erreurs : en ligne, boite, non, auto → contrôle l'insertion des catégories d'erreurs
    z.configuration.c_err_categories = z.nettoie(args["categorias errors"] or args["categoria errors"] or args["categories erreurs"] or args["categorie erreurs"]) or nil
    if (z.configuration.c_err_categories == nil) then
        z.configuration.c_err_categories = data.defauts.err_categories -- mode par défaut
    end
    -- validation du paramètre
    if (z.configuration.c_err_categories == "bóstia") then
        z.configuration.c_err_categories = "bóstia"
    end
    if (z.configuration.c_err_categories ~= "en linha" and z.configuration.c_err_categories ~= "bóstia" and z.configuration.c_err_categories ~= "non" and z.configuration.c_err_categories ~= "auto") then
        return briques.t_erreur("Lo paramètre nomenat ''categorias errors'' pòt pas prene que las valors ''en linha'', ''bóstia'', ''non'' o ''auto''.", "categorias errors", z.configuration)
    end
 
    -- - paramètres de debug (réservés aux développeurs)
    -- paramètre "raw" : sortie brute, non interprétée, du résultat (debug)
    z.configuration.c_raw = z.nettoie(args["raw"]) or nil
    if (z.configuration.c_raw == nil) then
        z.configuration.c_raw = data.defauts.raw
    end
    if (z.configuration.c_raw == "òc") then
        tx = "<nowiki>"
    end
    -- paramètre "debug" : ne fait presque rien actuellement
    z.configuration.c_debug = z.nettoie(args["debug"]) or nil
    if (z.configuration.c_debug == nil) then
        z.configuration.c_debug = data.defauts.debug
    end
    -- paramètre "force titre" : fait comme si c'était un article dont le titre est indiqué
    z.configuration.force_titre = z.nettoie(args["fòrça titol"]) or nil
 
 
    -- on analyse le règne indiqué, pour validation/préparation
    z.configuration.regne_affiche = data.premier_regne(z.configuration.regne) -- le nom wikif du règne
    if ( nil == z.configuration.regne_affiche or "" == z.configuration.regne_affiche ) then
        -- ce n'est pas un règne valide
        return briques.t_erreur("Lo paramètre nomenat ''règne'' es pas valid (" .. z.configuration.regne .. ").", "règne", z.configuration) .. z.box_erreurs("òc") -- on force l'erreur
    end
    local tmp2 = data.est_rang("règne")
    z.configuration.regne_lien = tmp2[2][1]
 
 
    -- on récupère les informations externes sur l'article en cours
    if (z.configuration.force_titre ~= nil) then
        -- dans ce cas on utilise ce titre-là
        z.configuration.titre_page = z.configuration.force_titre
    else
        z.configuration.titre_page = frame:preprocess("{{PAGENAME}}") -- titre de la page
    end
 
    -- on génère la table qui décrit la taxobox
    nb_lignes = z.traite_parametres(pframe)
    if (nb_lignes == nil or nb_lignes <= 0) then
        -- une taxobox vide !
        return briques.t_erreur("La taxobox es voida", "sintaxi", z.configuration) .. z.box_erreurs("òc") -- on force l'erreur
    end
 
 
    -- on extrait les taxons (nécessaire pour savoir de qui on parle)
    nb_taxons = z.cherche_taxons()
    if (nb_taxons <= 0) then
        return briques.t_erreur("Pas cap d'entrada ''taxon'' de provesida", "sintaxi", z.configuration) .. z.box_erreurs("òc") -- on force l'erreur
    end
 
    -- on détermine les titres et autres
    z.cherche_titres(frame)
 
    -- on parcours les lignes de structuration pour générer la sortie
    for i = 1, nb_lignes do
        tmp = z.traite_ligne(descriptions[i])
        if (tmp == nil) then
            -- erreur qui ne peut être ignorée
            return briques.t_erreur("Error de linha ''començament'' o ''filogenia bendèl", "sintaxi", z.configuration) .. z.box_erreurs("òc") -- on force l'erreur
        end
        tx = tx .. tmp
    end
    -- si la taxobox n'est pas fermée on la ferme
    if (fermeture_taxobox == false) then
        z.traite_fin() -- fermeture implicite, rendant donc "fin" optionnel
    end
 
    -- tout c'est passé correctement, on modifie le titre/sostitre
 
    -- si présent, et si article, on traite le sostitre
    if (z.configuration.sous_titre == nil) then
        pdebug = pdebug .. "sostítol = <nil><br/>"
    else
        pdebug = pdebug .. "sostítol = " .. z.configuration.sous_titre .. "<br/>"
    end
 
    local boite_titres = ""
    if (z.configuration.sous_titre ~= nil and z.configuration.sous_titre ~= "") then
        -- on détermine si la gestion du sostitre est active
        local t = false
        local b = false
        if (z.configuration.c_sous_titre == "non") then
            t = false
        elseif (z.configuration.c_sous_titre == "òc") then
            t = true
        else
            if (z.configuration.article == true) then
                t = true
            else
                t = false
                b = true -- boîte
            end
        end
        if (z.configuration.c_sous_titre == "bóstia" or b == true) then
            boite_titres = boite_titres .. "Sostítol :<br/>" .. z.configuration.sous_titre .. "<br/>"
        else
            if (t == true) then
                tx = tx .. briques.t_sous_titre(z.configuration.sous_titre)
            end
        end
    else
        if (z.configuration.c_sous_titre == "bóstia" or (z.configuration.c_sous_titre == "auto" and z.configuration.article == false)) then
            boite_titres = boite_titres .. "Sostítol :<br/>&nbsp;&nbsp;''pas cap''<br/>"
        end
    end
 
    if (z.configuration.titre_article == nil) then
        pdebug = pdebug .. "títol_article = <nil><br/>"
    else
        pdebug = pdebug .. "títol_article = " .. z.configuration.titre_article .. "<br/>"
    end
 
    -- si présent, et si article, on traite la modification du titre
    if (z.configuration.titre_article ~= nil and z.configuration.titre_article ~= "") then
        -- est-ce qu'on doit modifier ?
        local t = false
        local b = false
        if (z.configuration.c_titre == "òc") then
            t = true
        elseif (z.configuration.c_titre == "non") then
            t = false
        else -- auto
            if (z.configuration.article == true) then
                t = true
            else
                t = false
                b = true -- mode boîte si auto
            end
        end
        if (z.configuration.c_titre == "bóstia" or b == true) then
            boite_titres = boite_titres .. "Titol :<br/>" .. z.configuration.titre_article .. "<br/>"
        else
            if (t == true) then
                local modif = "{{DISPLAYTITLE:" .. z.configuration.titre_article .. "}}"
                tx = tx .. frame:preprocess(modif)
            end
        end
    else
        if (z.configuration.c_titre == "bóstia" or (z.configuration.c_titre == "auto" and z.configuration.article == false)) then
            boite_titres = boite_titres .. "Titol :<br/>&nbsp;&nbsp;''pas cap''<br/>"
        end
    end
 
    -- on insert les catégories et les erreurs
    tx = tx .. z.box_categories()
    tx = tx .. z.box_erreurs(z.configuration.c_erreurs)
    -- on insert la boîte des titres (à faire en fonction)
    if (boite_titres ~= "") then
        tx = tx .. briques.t_debut("", "Títol/sostítol")
        tx = tx .. "<small>" .. boite_titres .. "</small>"
        tx = tx .. briques.t_end()
    end
 
 
    -- spécial : le mode "raw" nécessite quelques ajustements
    if (z.configuration.c_raw == "òc") then
        tx = tx .. "</nowiki>"
        return frame:preprocess(tx)
    end
 
    if (z.configuration.c_debug == "òc") then
        return data.message .. tx .. pdebug -- cette partie seulement si demandée
    else
        return data.message .. tx
    end
end
 
 
local sup = ""
 
-- recupère le contenu du modèle suivant complet
function z.extrait_modele(texte, depart)
 
    local cnt = 0
    local pos = depart
    local npos
    local debut = nil
    local fin = nil
    local t
 
    if (depart > string.len(texte)) then
        return nil, nil
    end
 
    debut = string.find(texte, "{{", pos, true)
    if (debut == nil) then
        return nil, nil
    end
    cnt = 1
    while (true) do
        local p1 = string.find(texte, "{{", pos+2, true)
        local p2 = string.find(texte, "}}", pos+2, true)
        if (p2 == nil) then
            return nil, nil
        end
        if (p1 == nil) then
            cnt = cnt - 1
            npos = p2+2
            if (cnt == 0) then
                return debut, p2+1
            end
        elseif (p1 < p2) then
            cnt = cnt + 1
            npos = p1+2
        else
            cnt = cnt - 1
            npos = p2+2
            if (cnt == 0) then
                return debut, p2+1
            end
        end
        pos = npos
    end
end

-- cherche le | suivant, en ignorant ceux dans des modèles ou wikiliens
-- retourne une position (utf8)
function z.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 z.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 = z.pipe_suivant(ligne, courant)
    end
 
    return tbl
end
 
-- cette fonction reçoit (non processé) une taxobox et retourne la même chose
-- avec la syntaxe actuelle
function z.convertisseur(frame)
    local tx = "{{user:Hexasoft/Scribunto/Taxobox\n"
    local txt = [=[
{{Taxobox début | algue | une_image.jpg | légende | classification=AlgaeBASE }}
{{Taxobox | sosrègne | Hacrobia }}
{{Taxobox | sosrègne | Hacrobia (truc) | Hacrobia}}
{{Taxobox | sosrègne | Hacrobia | ancien=oui }}
{{Taxobox taxon | algue | embrancament | Haptophyta | <autors>, <data> }}
{{Taxobox taxon | algue | embrancament | Haptophyta | <autors>, <data> }}
{{Taxobox taxon | algue | embrancament | Haptophyta | <autors>, <data> }}
{{Taxobox taxons |
* classa ''[[Pavlovophyceae]]''
** òrdre ''[[Pavlovales]]''
* classa ''[[Prymnesiophyceae]]'' Hibberd, 1976
** òrdre ''[[Prymnesiales]]''
** òrdre ''[[Phaeocystales]]''
** òrdre ''[[coccolithophore|Isochrysidales]]''
** òrdre ''[[coccolithophore|Coccolithales]]''
}}
{{taxobox synonymes |
* ''Turlu tutu'' <small>Chapeau Pointu</small> }}
{{Taxobox UICN | LC | 1Ab12-coulé | chez moi }}
{{Taxobox UICN | VU | 1Ab12-coulé | ailleurs }}
{{Taxobox position | {{Phylogénie Eukaryota}} | Cryptophyta }}
{{Taxobox fin}}
]=]
    local ligne
    local tbl = {}
 
    debut, fin = z.extrait_modele(txt, 1)
    if (debut == nil) then
        return "''Sintaxi probablament erronèa.''<br/>" .. sup
    end
    while (debut ~= nil) do
        -- on récupère la ligne
        ligne = string.sub(txt, debut+2, fin-2)
        -- on découpe selon les | présents
        tbl = z.decoupe_pipe(ligne)
        if (tbl[1] ~= nil) then
 
            -- on analyse le premier
            local tmp = z.nettoie(string.lower(tbl[1]))
            if (tmp == "taxobox començament") then
                -- le règne
                regne = z.nettoie(tbl[2])
                tx = tx .. "|règne=" .. regne .. "\n"
                -- les autres parametres
                tx = tx .. "|començament "
                local i = 3
                while (tbl[i] ~= nil) do
                    local rr = string.find(tbl[i], "=")
                    if (rr == nil) then
                        tx = tx .. "|" .. z.nettoie(tbl[i]) .. " "
                    else
                        -- on remplace le = par un |
                        tx = tx .. "|" .. z.nettoie(string.gsub(tbl[i], "=", "| ")) .. " "
                    end
                    i = i + 1
                end
                tx = tx .. "\n"
            elseif (tmp == "taxobox filogenia bendèl") then
                tx = tx .. "|filogenia bendèl "
                local i = 2
                while (tbl[i] ~= nil) do
                    local rr = string.find(tbl[i], "=")
                    if (rr == nil) then
                        tx = tx .. "|" .. z.nettoie(tbl[i]) .. " "
                    else
                        -- on remplace le = par un |
                        tx = tx .. "|" .. z.nettoie(string.gsub(tbl[i], "=", "| ")) .. " "
                    end
                    i = i + 1
                end
                tx = tx .. "\n"
            elseif (tmp == "taxobox") then
                tx = tx .. "|rang "
                local i = 2
                while (tbl[i] ~= nil) do
                    local rr = string.find(tbl[i], "=")
                    if (rr == nil) then
                        tx = tx .. "|" .. z.nettoie(tbl[i]) .. " "
                    else
                        local p1 = mw.ustring.match(tbl[i], "^%s*(%a*)%s*=")
                        if (p1 == "ancian") then
                            tx = tx .. "|ancian "
                        else
                            -- on remplace le = par un |
                            tx = tx .. "|" .. z.nettoie(string.gsub(tbl[i], "=", "| ")) .. " "
                        end
                    end
                    i = i + 1
                end
                tx = tx .. "\n"
            elseif (tmp == "taxobox fin") then
                tx = tx .. "|fin\n"
            elseif (tmp == "taxobox taxon") then
                tx = tx .. "|taxon "
                local i = 3
                while (tbl[i] ~= nil) do
                    local rr = string.find(tbl[i], "=")
                    if (rr == nil) then
                        tx = tx .. "|" .. z.nettoie(tbl[i]) .. " "
                    else
                        -- obsolète ou nouveau ?
                        local p1 = mw.ustring.match(tbl[i], "%s*(%a*)%s*=")
                        if (p1 == "obsolèt") then
                            tx = tx .. "| obsolèt "
                        else
                            -- on remplace le = par un |
                            tx = tx .. "|" .. z.nettoie(string.gsub(tbl[i], "=", "| ")) .. " "
                        end
                    end
                    i = i + 1
                end
                tx = tx .. "\n"
            elseif (tmp == "taxobox imatge") then
                tx = tx .. "|imatge "
                local i = 2
                while (tbl[i] ~= nil) do
                    local rr = string.find(tbl[i], "=")
                    if (rr == nil) then
                        tx = tx .. "|" .. z.nettoie(tbl[i]) .. " "
                    else
                        -- obsolète ou nouveau ?
                        local p1 = mw.ustring.match(tbl[i], "%s*(%a*)%s*=")
                        if (p1 == "separator") then
                            tx = tx .. "| separator "
                        else
                            -- on remplace le = par un |
                            tx = tx .. "|" .. z.nettoie(string.gsub(tbl[i], "=", "| ")) .. " "
                        end
                    end
                    i = i + 1
                end
                tx = tx .. "\n"
            elseif (tmp == "taxobox conflicte") then
                tx = tx .. "|conflicte "
                local i = 2
                while (tbl[i] ~= nil) do
                    local rr = string.find(tbl[i], "=")
                    if (rr == nil) then
                        tx = tx .. "|" .. z.nettoie(tbl[i]) .. " "
                    else
                        -- on remplace le = par un |
                        tx = tx .. "|" .. z.nettoie(string.gsub(tbl[i], "=", "| ")) .. " "
                    end
                    i = i + 1
                end
                tx = tx .. "\n"
            elseif (tmp == "taxobox reparticion començament") then
                tx = tx -- pas res a far perque inutile
            elseif (tmp == "taxobox reparticion fin") then
                tx = tx -- pas res a far perque inutile
            elseif (tmp == "taxobox reparticion legenda") then
                tx = tx .. "|reparticion legenda "
                local i = 2
                while (tbl[i] ~= nil) do
                    local rr = string.find(tbl[i], "=")
                    if (rr == nil) then
                        tx = tx .. "|" .. z.nettoie(tbl[i]) .. " "
                    else
                        -- on remplace le = par un |
                        tx = tx .. "|" .. z.nettoie(string.gsub(tbl[i], "=", "| ")) .. " "
                    end
                    i = i + 1
                end
                tx = tx .. "\n"
            elseif (tmp == "taxobox uicn") then
                tx = tx .. "|uicn "
                local i = 2
                while (tbl[i] ~= nil) do
                    tx = tx .. "|" .. z.nettoie(tbl[i]) .. " "
                    i = i + 1
                end
                tx = tx .. "\n"
            elseif (tmp == "taxobox legenda") then
                tx = tx .. "|legenda "
                local i = 2
                while (tbl[i] ~= nil) do
                    tx = tx .. "|" .. z.nettoie(tbl[i]) .. " "
                    i = i + 1
                end
                tx = tx .. "\n"
            elseif (tmp == "taxobox parents") then
                tx = tx .. "|parents "
                local i = 2
                while (tbl[i] ~= nil) do
                    tx = tx .. "|" .. z.nettoie(tbl[i]) .. " "
                    i = i + 1
                end
                tx = tx .. "\n"
            elseif (tmp == "taxobox cites") then
                tx = tx .. "|cites "
                local i = 2
                while (tbl[i] ~= nil) do
                    tx = tx .. "|" .. z.nettoie(tbl[i]) .. " "
                    i = i + 1
                end
                tx = tx .. "\n"
            elseif (tmp == "taxobox taxons") then
                tx = tx .. "|taxons "
                if (tbl[2] ~= nil) then
                    tx = tx .. "|" .. tbl[2] .. " "
                end
                if (tbl[3] ~= nil) then
                    tx = tx .. "|" .. tbl[3] .. " "
                end
                tx = tx .. "\n"
            elseif (tmp == "taxobox sinonims") then
                tx = tx .. "|sinonims "
                if (tbl[2] ~= nil) then
                    tx = tx .. "|" .. tbl[2] .. " "
                end
                tx = tx .. "\n"
            elseif (tmp == "taxobox posicion") then
                tx = tx .. "|posicion "
                if (tbl[2] ~= nil) then
                    tx = tx .. "|" .. tbl[2] .. " "
                end
                if (tbl[3] ~= nil) then
                    tx = tx .. "|" .. tbl[3] .. " "
                end
                tx = tx .. "\n"
            else
                tx = tx .. "|Element desconegut (" .. tmp .. ")\n"
            end

        end
        -- le suivant
        cur = fin + 1
        debut, fin = z.extrait_modele(txt, cur)
    end
 
    tx = tx .. "}}"
    
    tx = "<pre>" .. txt .. "</pre>\n\n" .. "<pre>" .. tx .. "</pre>\n\n" .. tx .. "\n"
-- reste à faire : phylo arbre, phylo inexistant, reparticion, reparticion image
    return frame:preprocess(tx)
end

-- cette fonction génère une erreur d'exécution
function z.fonction_en_erreur(frame)
    local t = string.find(nil, nil)
end

function z.mw_title(frame)
    res = ""
    -- afficher le contenu de mw.title
    for k, v in pairs( mw.title ) do
        res = res .. "mw.title: " .. k .. " → " .. type(v) .. "<br/>\n"
    end

    return res
end


return z