Module:Outils

De Lagny-sur-Marne Wiki
Révision datée du 16 septembre 2013 à 13:15 par Hexasoft (discussion) (typo)
Aller à : navigation, rechercher

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

local mw = mw or require 'mw'  -- pour debugage sous ecllipse

local Outils = { }


-- trim nettoie un paramètre non nommé (supprime les espaces et retours ligne au début et à la fin)
-- retourne  nil si le texte est vide ou n'est pas du texte. Les nombres ne sont PAS considérés comme du texte.
function Outils.trim( texte )
    if type( texte ) == 'string' and texte ~= '' then
        return mw.text.trim( texte )
    end
end

-- erreur génère une erreur
function Outils.erreur( texte )
    return '<span class="error">' .. (texte or "''aucune erreur indiquée''") .. "</span>"
end

-- validTextArg renvoit le premier paramètre chaine non vide
-- Paramètre : 
--      1 - tableau contenant tous paramètres
--      2, ... - les noms des paramètres qui doivent êtres testés.
function Outils.validTextArg( args, name, ... ) 
    local texte = Outils.trim( args[name] )
    if texte then
        return texte
    end
    if select( '#', ... ) > 0 then
        return Outils.validTextArg( args, ... )
    end
end

-- notEmpty renvoie le premier paramètre non vide ou nul. 
-- Paramètre : 
--      1, ... - les variables qui doivent êtres testés.
function Outils.notEmpty( var, ... )
    local tvar = type( var )
    
    if tvar == 'string' and var ~= '' then
        return mw.text.trim( var )
    elseif tvar == 'table' then
        local nextFunc = pairs( var )   -- n'utilise pas next car non défini par mw.loadData
        if nextFunc( var ) ~= nil then
            return var
        end 
    elseif var == true or ( tvar == 'number' and var ~= 0 ) or tvar == 'function' then
        return var
    end
    
    if select( '#', ... ) > 0 then
        return Outils.notEmpty(  ... )
    end
end

-- extractArgs permet de récupérer les arguements du modèle, 
-- ou la table transmise à la fonction par une autre fonction d'un module
-- Paramètres : 
--      1 - un objet frame ou une table contenant les paramètre
--      2, ...  - une liste de nom de paramètre pour déterminé si les paramètres sont transmis par #invoke:
--          le premier paramètre sera systématiquement testé.
function Outils.extractArgs ( frame, ... )
    if type( frame ) == 'table' then
        if type( frame.getParent ) == 'function' then
            if Outils.validTextArg( frame.args, 1, ...) then
                return frame.args
            else
                local args = frame:getParent().args;
                for k,v in pairs( frame.args ) do
                    args[k] = v;
                end
                return args
            end
        else
            return frame 
        end
    else
        return { frame, ... }
    end
end

-- abr génère une abréviation (discrète par défaut)
-- paramètres : 
--      1 = abréviation, 
--      2 = texte, 
--      3 = langue, 
--      nbsp =  '-' pour une espace insécable avant l'abréviation, '+' pour l'avoir après.
--      visible = true pour une abréviation non discrète
function Outils.abr( frame )
    local args = Outils.extractArgs( frame )
    if args[2] == nil then 
        return args[1] or ''                 -- retoune l'abréviation ou au minimum une chaine vide s'il n'y a pas de texte
    end

    local wikiText = { '<abbr' }
    if not args.visible then
        table.insert( wikiText, ' class="abbr"' )
    end
    table.insert( wikiText, ' title="' )
    table.insert( wikiText, args[2] )
    if args[3] then 
        table.insert( wikiText, '" lang="' )
        table.insert( wikiText, args[3] )
    end
    table.insert( wikiText, '">' )
    table.insert( wikiText, args[1] )
    table.insert( wikiText, '</abbr>' )
    if args.nbsp == '-' then
       table.insert( wikiText, 1, '&nbsp;' )
    elseif args.nbsp == '+' then
       table.insert( wikiText, '&nbsp;' )
    end

    return table.concat( wikiText )
end

-- ordinal renvoie une chaine correspondant à l'abréviation de l'adjectif ordinal du nombre.
-- Paramètres :
--        1 = nombre (string ou number) 
--        2 = true pour avoir première au lieu de premier su nombre = 1
function Outils.ordinal( nombre, feminin )
    local num = tonumber( nombre )
    if num == nil then
        return Outils.trim( tostring( nombre ) ) or ''
    elseif num == 1 then
        if feminin then
            return Outils.abr{ '1<sup>re</sup>', 'première' }
        else
            return Outils.abr{ '1<sup>er</sup>', 'premier' }
        end
    else
        local nom = mw.loadData( 'Module:Outil/Data' ).ordinal
        if nom[num] then
            return Outils.abr{ num .. '<sup>e</sup>', nom[num] }
        else
            return num .. '<sup>e</sup>'
        end
    end
end

--[[
  Fonction de traitement d'une "tranche" de nombres entre 0 et 999.
  Retourne la forme texturelle (5 → cinq, 46 → quarante six, 432 → quatre cent trente deux…)
  Les paramètres sont les chiffres, du plus grand au plus petit (centaine, dizaine, unité).
  La valeur nil signifie "0" (pour n'importe lequel des paramètres)
  La fonction retourne le texte ou 'nil' si la valeur est zéro (pour gérer les 0 millier…)
  Le paramètre 'langue' indique la variante de langue (fr, be, ch ou ch2).
--]]
function Outils.traite_tranche(_c1, _c2, _c3, langue)
	if (_c1 == nil) then c1 = 0 else c1 = tonumber(_c1) or 0 end
	if (_c2 == nil) then c2 = 0 else c2 = tonumber(_c2) or 0 end
	if (_c3 == nil) then c3 = 0 else c3 = tonumber(_c3) or 0 end

	if (c1 == 0 and c2 == 0 and c3 == 0) then
		return nil -- sil signifie "zéro" (mais à traiter spécialement quand entouré)
	end
	local resu = ""
	
	-- on calcule la valeur restante (sans les centaines)
	local val = 10*c2 + c3
	-- présence d'une centaine ?
	if (c1 ~= 0) then
		if (c1 == 1) then
			resu = "cent " -- séparateur
		else
			-- plusieurs centaines : on ajoute l'unité
			resu = Data.infcent[c1] .. " cent"
			-- si pas d'unité 100 prend un 's'
			if (val == 0) then
				resu = resu .. "s "
			else
				resu = resu .. " "
			end
		end
	end
	-- reste = 0 ?
	if (val == 0) then
		-- on retourne directement la centaine
		return resu
	end
	-- c'est forcément un nombre pré-défini
	local vvv
	if (langue == "fr") then
		vvv = Data.infcent[val]
	elseif (langue == "be") then
		vvv = Data.infcent_be[val] or Data.infcent[val]
	elseif (langue == "ch") then
		vvv = Data.infcent_ch[val] or Data.infcent_be[val] or Data.infcent[val]
	else
		vvv = Data.infcent_ch2[val] or Data.infcent_be[val] or Data.infcent[val]
	end
	return resu .. vvv .. " "
	-- note : cette fonction retourne *toujours* un " " à la fin du terme
end

--[[
  Fonction principale
  Reçoit en paramètre (premier non nommé) le nombre à traiter.
  Retourne la forme textuelle de ce nombre.
--]]
function Outils.nombre2texte(frame)
	-- on traite les paramètres nommés dans l'ordre frame puis pframe afin de permettre
	-- de créer des modèles qui surchargent certains paramètres
	local args = frame.args
	local pargs = frame:getParent().args

	-- le nombre à convertir (vient toujours du modèle)
	local valeur = pargs[1]
	if (valeur == nil) then
		return Outils.erreur("Il faut un paramètre non nommé numérique.")
	end
	
	-- s'il y a une virgule, on l'ignore
	local bla = mw.ustring.find(valeur, "[.,]")
	if (bla ~= nil) then
		-- extraction de la partie avant la virgule
		valeur = mw.ustring.match(mw.text.trim(valeur), "^[-]?[0-9]*")
	end

	nvaleur = tonumber(valeur)
	if (type(nvaleur) ~= "number") then
		return Outils.erreur("Le paramètre doit être un nombre.")
	end
	-- limites
	if (nvaleur < -999999999999 or nvaleur > 999999999999) then
		return Outils.erreur("Nombre trop grand ou trop petit.")
	end
	-- note : ici il faudrait s'assurer que le nombre est un entier !

	-- on extrait le moins si présent
	signe = false
	if (nvaleur < 0) then
		nvaleur = -nvaleur
		signe = true
	end

	-- option : choix de la langue
	local langue = args["langue"] or pargs["langue"]
	if (langue == nil) then
		langue = "fr"
	else
		langue = mw.text.trim(langue)
	end
	-- validation des valeurs permises
	if (langue ~= "fr" and langue ~= "be" and langue ~= "ch" and langue ~= "ch2") then
		return Outils.erreur("Paramètre langue non reconnu (fr, be, ch ou ch2).")
	end

	-- type de résultat : seule valeur autorisée : 'ordinal'
	local style = args["type"] or pargs["type"]
	if (style ~= nil and style ~= "ordinal") then
		style = nil
	end

	-- type d'orthographe
	local ortho = args["orthographe"] or pargs["orthographe"]
	if (ortho ~= nil and ortho ~= "réformée") then
		ortho = nil
	end

	-- cas (très) simple : 0
	if (nvaleur == 0) then
		if (style == "ordinal") then
			return "zéroième"
		else
			return "zéro"
		end
	end

	-- on traite les autres cas simples : le nombre est pré-codé
	local val
	if (langue == "fr") then
		val = Data.infcent[nvaleur]
	elseif (langue == "be") then
		val = Data.infcent_be[nvaleur] or Data.infcent[nvaleur]
	elseif (langue == "ch") then
		val = Data.infcent_ch[nvaleur] or Data.infcent_be[nvaleur] or Data.infcent[nvaleur]
	else
		val = Data.infcent_ch2[nvaleur] or Data.infcent_be[nvaleur] or Data.infcent[nvaleur]
	end

	local res = val or ""
	if (val == nil) then
		-- pas de résultat, on fait le "calcul"

		-- on l'éclate en une table des différents caractères
		local tvaleur = mw.text.split(valeur, "")
		local nb = #tvaleur -- nombre d'éléments

		-- on boucle sur les triplets de chiffres et on stocke le résultat dans une table
		local tbl = {}
		while (true) do
			-- on prend les 3 valeurs concernées
			local p1 = tvaleur[nb-2]
			local p2 = tvaleur[nb-1]
			local p3 = tvaleur[nb]
			-- si les 3 sont 'nil' on a terminé
			if (p1 == nil and p2 == nil and p3 == nil) then
				break
			end
			-- on calcule la valeur du bloc concerné (rangé dans la table)
			local tmp = mw.text.trim(Outils.traite_tranche(p1, p2, p3, langue) or "zéro")
			table.insert(tbl, tmp)
			-- décalage
			nb = nb - 3
		end

		-- on construit le résultat final en combinant les éléments
		-- et en ajoutant les milliers/millions/...
		local pos = 1
		while (tbl[pos] ~= nil) do
			local el = ""
			-- on l'ajoute, s'il existe
			if (tbl[pos] ~= "zéro " and tbl[pos] ~= "zéro") then
				if (pos == 1) then
					-- rang "1", on ajoute simplement la valeur
					el = tbl[pos] .. " "
				else
					-- si la valeur est "un" on ajoute seulement le rang
					if (tbl[pos] == "un " or tbl[pos] == "un") then
						el = Data.sup[pos] .. " "
					else
						-- on ajoute X + rang
						el = tbl[pos] .. " " .. Data.sup[pos]
						-- le pluriel, sauf pour 1000, et le séparateur
						if (pos ~= 2) then
							el = el .. "s "
						else
							el = el .. " "
						end
					end
				end
			end
			-- on insert
			res = el .. res

			-- on passe au suivant
			pos = pos + 1
		end

		-- suppression espaces
		res = mw.text.trim(res)

	end -- fin (si on n'avait pas trouvé en pré-défini)

	if (style ~= nil) then
		-- ordinal : on cherche la fin du nombre pour ajouter le "ième" qui convient
		if (res == "zéro") then
			res = "zéroième" -- eurk!
		elseif (res == "un") then
			res = "premier"
		else
			-- on récupère le dernier mot
			local fin = mw.ustring.match(res, "%a*$")
			-- on récupère le reste (début)
			local debut = mw.ustring.gsub(res, "%a*$", "")
			
			-- on génère la fin en ordinal
			local nfin = Data.iemes[fin]
			if (nfin == nil) then
				nfin = Outils.erreur("erreur interne d'ordinal.")
			end
			res = debut .. nfin
		end
	end

	-- si orthographe réformée on remplace les espaces par des tirets
	if (ortho == "réformée") then
		res = mw.ustring.gsub(res, "[ ]", "-")
	else
		-- sinon on remplace les espaces par des insécables
		res = mw.ustring.gsub(res, "[ ]", "&#160;")
	end
	if (style == nil) then
		-- traitement de signe éventuel (sauf ordinaux)
		if (signe) then
			res = "moins&#160;" .. res
		end
	end

	-- on retourne
	return res
end



return Outils