Modulo:Avixo
--Ce module implémente les modèles de bandeau.
--Standardisation des bandeaux ([[Categoria:Modèle de bandeau]]).
--Créer une fonction exportable pour le modèle {{Bandeau}} (ns:all).
--Créer une fonction exportable pour les bandeaux d'article (ns:0).
--Créer une fonction exportable pour les bandeaux de section (ns:0).
--Créer une fonction exportable pour les bandeaux d'ébauche (ns:0).
--Créer une fonction exportable pour les bandeaux de discussion (ns:1).
--Créer une fonction exportable pour les bandeaux système (ns:8).
--Gérer les images multiples.
local p = {}
local trim = require('Modulo:Outils').trim
local yesno = require('Modulo:yesno')
local class = mw.loadData('Modulo:Avixo/Clase')
local moduleEbauche = 'Modulo:Avixo/Spigaso'
-- local paramEbauche = mw.loadData(moduleEbauche) -- ne sera chargé que pour un bandeau d'ébauche.
local cfg = {
forme = 'bandeau-simple',
niveau = 'bandeau-niveau-neutre',
formatLien = '[[File:%s|%spx|alt=%s|class=noviewer]]',
tailleIcone = '45x45',
erreurArgument = 'Paramètre <code>|%s=</code> manquant',
erreurEbaucheParam = 'le thème « %s » du modèle [[Modèle:Ébauche|{{ébauche}}]] n’est pas [[Aide:Ébauche/Aide paramètres|défini]].',
erreurEbaucheType = 'le thème « %s » doit apparaître en tête de liste du modèle [[Modèle:Ébauche|{{ébauche}}]].',
ebaucheImage = '<span style="white-space:nowrap;word-spacing:5px">%s</span>',
ebaucheTitre = '%s est une [[Aide:Ébauche|ébauche]].',
ebaucheTitreSujet = '%s est une [[Aide:Ébauche|ébauche]] concernant %s.',
ebaucheCateg = '[[Categoria:Wikipédia:ébauche %s]]',
}
-- CSS inline pour éviter FOUC sur le site mobile
local rustineFoucMobile = {
['display'] = 'table-cell',
['padding-right'] = '0.5em',
}
local function erreur(texte, formatstring, tag)
local res = mw.html.create(tag or 'span')
res :addClass('error')
:wikitext('Erreur : ')
if formatstring then
res:wikitext(formatstring:format(texte))
else
res:wikitext(texte)
end
return tostring(res)
end
function p._bandeau(args)
local res = mw.html.create('div')
local cells = mw.html.create()
local icone = trim(args.image) or trim(args['icona'])
local backgroundIcone = class.icones[icone] or false
local texte = (trim(args.texte) or erreur('texte', cfg.erreurArgument))
res :attr{ id = args.id }
:addClass('bandeau-container')
:addClass(class.formes[args.forme] or cfg.forme)
:addClass(class.niveau[args.niveau] or cfg.niveau)
:addClass('plainlinks')
:addClass(args.class)
:cssText(args.style)
if yesno(args.centrer) then
cells = mw.html.create('div')
:addClass('bandeau-centrer')
elseif trim(args.droite) then
cells = mw.html.create('div')
:css{ display = 'table', width = '100%' }
end
if args['icona bma'] == 'oui' then
cells:wikitext( mw.getCurrentFrame():expandTemplate{ title = 'Bma' } )
end
if icone and not backgroundIcone then
local iconeWiki = icone
if not icone:match('%[') then
local alt = args.alt or args['légende'] or ''
if yesno( args['domaine public'] ) then
alt = alt .. '|link='
end
local taille = args['taille icona'] or cfg.tailleIcone
iconeWiki = cfg.formatLien:format(icone, taille, alt)
end
cells
:tag('div')
:addClass('bandeau-cell bandeau-icone')
:css(rustineFoucMobile)
:wikitext(iconeWiki)
:done()
end
-- pour que le texte commence sur une nouvelle ligne (parsage wikicode "*", etc.)
-- (et uniquement dans ce cas de figure, autrement le texte se retrouve encadré par un <p> automatique)
if texte:match('^[*#;:]') then
texte = '\n' .. texte
end
cells
:tag('div')
:addClass('bandeau-cell')
:css(rustineFoucMobile)
:addClass(backgroundIcone and ('bandeau-icone-css ' .. backgroundIcone) or nil)
:wikitext(texte)
:newline() -- ne pas supprimer, sert à la génération automatique de <p> encadrant le contenu
:done()
if trim(args.droite) then
cells
:tag('div')
:addClass('bandeau-cell')
:css(rustineFoucMobile)
:css( 'padding-left', '1em' )
:newline()
:wikitext(args.droite)
:done()
end
res :node(cells)
if trim(args['supplément']) then
res :tag('div')
:wikitext(args['supplément'])
:done()
end
return tostring(res)
end
function p._bandeauAvertissement(args)
local htmlTexte = mw.html.create()
local titre = trim(args.titre) or erreur('titre', cfg.erreurArgument)
local suffixeDate = trim(args.date)
if suffixeDate then
-- pour rendre insécable, seulement si le paramètre semble bien être une date valide
suffixeDate = (require 'Modulo:Date').modeleDate{suffixeDate, nolinks=true, onerror='input'}
titre = titre:gsub('%.$', '')
suffixeDate = ' <small>(' .. suffixeDate .. ').</small>'
elseif not titre:match('[.,;:!?]$') then
titre = titre .. '.'
end
htmlTexte
:tag('strong')
:addClass('bandeau-titre')
:wikitext(titre)
:done()
:wikitext(suffixeDate)
:newline():newline()
:wikitext(args.texte)
local parametres = {
forme = 'article',
niveau = args.niveau,
id = args.id,
class = 'bandeau-container metadata ambox ' .. ( args.class or '' ),
['icona'] = trim(args['icona']) or trim(args['icona-complexe']) or args.niveau,
alt = args.alt or args['légende'],
['domaine public'] = args['domaine public'],
texte = tostring(htmlTexte),
['icona bma'] = args['icona bma'],
['supplément'] = args['supplément'],
}
return p._bandeau(parametres)
end
--[[
TODO - fusionner ces deux méthodes :
* p._bandeau() avec forme=section
* p._bandeauSection()
]]--
function p._bandeauSection(args)
local res = mw.html.create('div')
local icone = trim(args.image) or trim(args['icona'])
local backgroundIcone = class.icones[icone] or false
local texte = (trim(args.texte) or erreur('texte', cfg.erreurArgument))
res :addClass('bandeau-container bandeau-section metadata')
:addClass(class.niveau[args.niveau] or cfg.niveau)
if icone and not backgroundIcone then
local iconeWiki = icone
if not icone:match('%[') then
local alt = args.alt or args['légende'] or ''
if yesno( args['domaine public'] ) then
alt = alt .. '|link='
end
iconeWiki = cfg.formatLien:format(icone, 'text-top|20x17', alt)
end
res :tag('div')
:addClass('bandeau-cell bandeau-icone')
:wikitext(iconeWiki)
:done()
end
res :tag('div')
:addClass('bandeau-cell')
:addClass(backgroundIcone and ('bandeau-icone-css ' .. backgroundIcone) or nil)
:wikitext(texte)
:done()
return tostring(res)
end
-- fonction qui inverse la casse du premier caractère d'une chaine
local function inverserCasse( str )
if type( str ) == 'string' then
local premierCar = mw.ustring.sub( str, 1, 1 )
if mw.ustring.lower( premierCar ) == premierCar then
return mw.ustring.upper( premierCar ) .. mw.ustring.sub( str, 2 )
else
return mw.ustring.lower( premierCar ) .. mw.ustring.sub( str, 2 )
end
end
end
-- fonction qui récupètre la ou les tables d'ébauche correspondant au thème
local function getEbaucheTable( paramEbauche, theme, feminin )
-- suprime les marques de direction ltr
theme = theme:gsub( '\226\128\142', '' ):gsub( '_', ' ' )
-- récupére les paramètres lié au theme, à partir du module:Bandeau/Ébauche
local params = {}
local ebauche = paramEbauche[ theme ] or paramEbauche[ inverserCasse( theme ) ]
if not ebauche and theme:find( ' ' ) then
-- teste si l'un des mots du thème correspond à un adjectif existant
for adj in theme:gmatch( ' ([^ ]+)' ) do
paramsAdj = getEbaucheTable( paramEbauche, adj, feminin )
if paramsAdj and paramsAdj.adjectif == true then
local nom = theme:gsub( ' ' .. adj:gsub( '(%p)', '%%%1'), '' )
params = getEbaucheTable( paramEbauche, nom, feminin )
if params then
return params, paramsAdj
end
end
end
-- aucun mot ne correspond à un adjectif, on essait une autre methode pour trouver une correspondance avec plusieurs mots
if theme:find( ' .+ ' ) then
for adj, paramsAdj in pairs( paramEbauche ) do
if paramsAdj.adjectif == true and theme:find( ' ' .. adj, 2, true ) then
local nom = theme:gsub( ' ' .. adj:gsub( '(%p)', '%%%1'), '' )
params = getEbaucheTable( paramEbauche, nom, feminin )
if params then
return params, paramsAdj
end
end
end
end
end
if feminin and ebauche and ebauche.feminin then
ebauche = paramEbauche[ ebauche.feminin ]
end
if ebauche then
for n, v in pairs( ebauche ) do
params[ n ] = v
end
else
params = nil
end
return params, nil
end
p.getEbaucheTable = getEbaucheTable
local function femininFromWikidata()
local entity = mw.wikibase.getEntity()
if entity then
local p31 = entity:getBestStatements( 'P31' )
local estHumain = type( p31 ) == 'table'
and #p31 == 1
and type( p31[ 1 ].mainsnak ) == 'table'
and type( p31[ 1 ].mainsnak.datavalue ) == 'table'
and type( p31[ 1 ].mainsnak.datavalue.value ) == 'table'
and p31[ 1 ].mainsnak.datavalue.value['numeric-id'] == 5
local p21 = entity:getBestStatements( 'P21' )
local estFeminin = type( p21 ) == 'table'
and #p21 == 1
and type( p21[ 1 ].mainsnak ) == 'table'
and type( p21[ 1 ].mainsnak.datavalue ) == 'table'
and type( p21[ 1 ].mainsnak.datavalue.value ) == 'table'
and p21[ 1 ].mainsnak.datavalue.value['numeric-id'] == 6581072
return estHumain, estFeminin
end
return false, false
end
p['_ébauche'] = function ( args )
local paramEbauche = mw.loadData( moduleEbauche )
local page = mw.title.getCurrentTitle()
local ebauches, gestionErreur = {}, {}
local humain, feminin = femininFromWikidata()
feminin = yesno( args["féminin"], true, false ) or feminin
local estFeminin
-- fonction qui retourne la valeur de param pour l'ébauche i, ou une valeur par défaut
local ebaucheParam = function( i, param )
return ebauches[ i ] and ebauches[ i ][ param ] or paramEbauche[''][ param ]
end
-- récupération des paramètres de tous les thèmes
for i, theme in ipairs( args ) do
theme = trim( theme )
if theme then
local t, tAdj = getEbaucheTable( paramEbauche, theme, feminin )
if t then
table.insert( ebauches, t )
table.insert( ebauches, tAdj )
else
table.insert(
gestionErreur,
erreur( theme, cfg.erreurEbaucheParam, 'div' )
)
end
end
end
-- récupération des différents titres, images et catégories
local images, titres, categs = {}, {}, {}
local tailleIcone = '45x35'
if #ebauches > 3 then
tailleIcone = '35x25'
end
for i, ebauche in ipairs( ebauches ) do
-- création du lien de l'image
local alt = ''
if ebauche.icone then
local image = cfg.formatLien:format( ebauche.icone, tailleIcone, alt )
table.insert( images, image )
end
if math.fmod( #ebauches, 3 ) == 1 and ( #ebauches - i ) == 2
or math.fmod( i, 3 ) == 0 and ( #ebauches - i ) > 1
then
-- sur plusieurs lignes s'il y a plus de 3 images, avec minimum deux images sur la dernière ligne
table.insert( images, '<br> ' )
end
if i > 1 and ebauche.type and ebauche.type ~= paramEbauche[''].type then
-- remplace "Cet article par "Ce portail" ou autre en fonction du premier thème
table.insert(
gestionErreur,
erreur( ebauche.nom, cfg.erreurEbaucheType, 'div' )
)
end
-- récupères les différents noms de thème
if ebauche.adjectif and #titres > 0 then
local sujet = ebauche.sujet or ebauche.nom
if estFeminin then
sujet = ebauche.sujetF or sujet:gsub(
ebauche.nom:gsub( '(%p)', '%%%1') .. '%f[%W]',
ebauche.feminin
)
end
-- ajout du sujet de l'adjectif dans le sujet de l'ébauche précédente
local titre, subst = titres[ #titres ]:gsub(
'<(adj[^>]*)>',
{ adjectif = sujet, adj = ebauche.nom, adjF = ebauche.feminin }
)
if subst > 0 then
titres[ #titres ] = titre
else
titres[ #titres ] = titre .. ' ' .. sujet
end
else
table.insert( titres, ebauche.sujet )
estFeminin = ebauche.estFeminin or ( ebauche.sujet == '' and estFeminin )
end
-- mise en forme des catégories
if ebauche.adjectif then
-- tentative d'ajout du nom de l'adjectif dans les catégories précédentes
local modif = false
for k, v in ipairs( categs ) do
local cat, subst = v:gsub(
'<(adj[^>]*)>',
{ adj = ebauche.nom, adjF = ebauche.feminin, adjectif = ebauche.nom }
)
if subst == 0 then
cat = v .. ' ' .. ebauche.nom
end
if mw.title.new( 'Categoria:Wikipédia:ébauche ' .. cat ).exists then
categs[ k ] = cat
modif = true
end
end
if not modif
and humain
and mw.title.new( 'Categoria:Wikipédia:ébauche personnalité ' .. ebauche.feminin ).exists
then
table.insert( categs, 'personnalité ' .. ebauche.feminin )
end
end
table.insert( categs, ebauche.categ )
end
-- mise en forme des images
local image
if trim( args['icona'] ) then
local theme = getEbaucheTable( paramEbauche, args['icona'] )
if theme and theme.icone then
image = cfg.formatLien:format(
theme.icone,
tailleIcone,
theme.altIcone or ( 'image illustrant ' .. theme.sujet )
)
end
elseif #images == 1 then
image = images[ 1 ]
elseif #images > 1 then
image = cfg.ebaucheImage:format( table.concat( images, ' ' ) )
end
-- mise en forme du titre
local titre
if #titres > 0 then
titre = cfg.ebaucheTitreSujet:format(
ebaucheParam( 1, 'type' ),
mw.text.listToText( titres )
)
else
titre = cfg.ebaucheTitre:format( ebaucheParam( 1, 'type' ) )
end
-- mise en forme du texte
local texte
if #ebauches == 0 then
texte = ( ebaucheParam( 1, 'message' ) ) .. '.'
else
local message = ebaucheParam( 1, 'message' )
local selon = ebaucheParam( 1, 'selon' )
-- ajout d'un point si le paramètre selon commence par un retour ligne ou une majuscule
if message:sub( -1 ) == ')' and ( selon:sub( 1, 3 ) == '<br' or mw.ustring.match( selon, '^%u' ) ) then
texte = ( ebaucheParam( 1, 'message' ) ) .. '. ' .. ( ebaucheParam( 1, 'selon' ) ) .. '.'
else
texte = ( ebaucheParam( 1, 'message' ) ) .. ' ' .. ( ebaucheParam( 1, 'selon' ) ) .. '.'
end
end
-- ajout d'un texte s'il y a une liste de tâches
local todo = page.talkPageTitle and mw.title.new( page.talkPageTitle.fullText .. '/À faire' ) or nil
if todo and todo.exists then
texte = texte .. '\n\n'
.. 'Consultez la liste des <b>tâches à accomplir</b> en [['
.. page.talkPageTitle.prefixedText
.. '|page de discussion]].'
end
-- paramètres pour le bandeau
local parametres = {
niveau = 'ébauche',
['icona'] = image,
titre = titre:gsub( ' <adj[^>]*>', '' ),
texte = texte,
id = args.id
}
-- concaténation des différentes catégories (pas de catégorisation si nocat, ou page de discussion, ou espace utilisateur)
local categ = ''
local messageErreur = table.concat( gestionErreur )
if not ( yesno( args.nocat, true, false ) or page.isTalkPage or page.namespace == 2 ) then
for i = 1, #categs do
categs[ i ] = cfg.ebaucheCateg:format( categs[ i ] )
end
categ = table.concat( categs ):gsub( ' <[^>]*>', '' )
if categ == '' then
categ = cfg.ebaucheCateg:format('')
end
if #gestionErreur > 0 then
messageErreur = messageErreur .. '[[Categoria:Ébauche inconnue]]'
end
end
return p._bandeauAvertissement( parametres ) .. messageErreur .. categ
end
---
-- fonction retournant un paramètre d'une table d'ébauche.
-- Elle est prévue pour être appelée directement par #invoke:
-- avec pour paramètres le thème et le paramètre désiré
-- Cette fonction est principalement destinée à la page d'aide.
function p.parametreEbauche( frame )
local paramEbauche = mw.loadData( moduleEbauche )
local theme = frame.args[1]
local param = frame.args[2]
if paramEbauche[ theme ] then
return paramEbauche[ theme ][ param ]
elseif paramEbauche[ inverserCasse( theme ) ] then
return paramEbauche[ inverserCasse( theme ) ][ param ]
end
end
---
-- Fonction retournant le féminin d'un théme d'ébauche
-- Elle est prévue pour être appelée directement par #invoke:
-- avec pour paramètres le thème et le paramètre désiré
-- Cette fonction est principalement destinée au modèle {{Categoria d'ébauche}}, donc une valeur n'est retournée que si les catégories sont identiques
function p.femininEbauche( frame )
local paramEbauche = mw.loadData( moduleEbauche )
local theme = frame.args[1]
local themeF
if theme then
local t = getEbaucheTable( paramEbauche, theme )
local tF, tAdjF = getEbaucheTable( paramEbauche, theme, true )
if t.feminin and tF.categ == t.categ then
if tAdjF then
themeF = tF.nom .. ' ' .. tAdjF.feminin
elseif tF then
themeF = tF.nom
end
return themeF
end
end
end
---
-- fonction qui contruit deux tableaux récapitulatif de l'ensemble des paramètres d'ébauche
function p.tableParametresEbauches( frame )
local paramEbauche = mw.loadData( moduleEbauche )
local currentFrame = mw.getCurrentFrame()
local languageObj = mw.getContentLanguage()
function pagesInCategory( category )
return languageObj:formatNum( tonumber( currentFrame:callParserFunction( 'PAGESINCATEGORY', { category, 'R' } ) ) )
end
local params, paramAdj, paramType = {}, {}, {}
for clef, ebauche in pairs( paramEbauche ) do
local kEbauche = {}
for k, v in pairs( ebauche ) do
kEbauche[ k ] = v
end
kEbauche.clef = clef
if ebauche.type then
table.insert( paramType, kEbauche )
elseif ebauche.adjectif then
if clef == ebauche.nom or clef ~= ebauche.feminin then
table.insert( paramAdj, kEbauche )
end
else
table.insert( params, kEbauche )
end
end
local comp = function( eb1, eb2 )
return eb1.clef < eb2.clef
end
table.sort( params, comp )
table.sort( paramAdj, comp )
table.sort( paramType, comp )
local wikiTab = { '<table class="wikitable sortable" style="table-layout:fixed;">' }
wikiTab.insert = function ( t, value )
table.insert( t, value )
return t
end
wikiTab
:insert('\n=== Ébauches normales ===\n')
:insert('<caption>Liste des paramètres d\'ébauche</caption>')
:insert('<th scope=col style="width:15%;">nom</th>')
:insert('<th scope=col style="width:50px; box-sizing:border-box;">i</th>')
:insert('<th scope=col style="width:20%;">sujet</th>')
:insert('<th scope=col style="width:20%;">catégorie</th>')
:insert('<th scope=col style="width:20%;">compteur</th>')
:insert('<th scope=col>selon</th>')
for k, ebauche in ipairs( params ) do
wikiTab
:insert('<tr><td>')
:insert( ebauche.clef )
:insert('</td><td>')
if ebauche.icone then
wikiTab:insert( cfg.formatLien:format( ebauche.icone, '45x35', ebauche.altIcone or '' ) )
end
wikiTab
:insert('</td><td>')
:insert( ebauche.sujet )
:insert('</td><td>')
if ebauche.categ then
wikiTab
:insert( '[[:Categoria:Wikipédia:ébauche ' .. ebauche.categ .. '|' .. ebauche.categ .. ']]' )
end
wikiTab
:insert('</td><td>')
if ebauche.categ then
wikiTab
:insert( pagesInCategory( 'Wikipédia:ébauche ' .. ebauche.categ ) )
end
wikiTab
:insert('</td><td>')
:insert( ebauche.selon )
:insert('</td></tr>')
end
wikiTab:insert('</table>')
-- seconde table pour les adjectifs
wikiTab
:insert('\n=== Adjectifs ===\n')
:insert('<table class="wikitable">')
:insert('<caption>Liste des adjectifs</caption>')
:insert('<th scope=col>adjectif</th>')
:insert('<th scope=col>féminin</th>')
:insert('<th scope=col style="width:50px;">icone</th>')
:insert('<th scope=col>sujet</th>')
:insert('<th scope=col>sujet féminin</th>')
:insert('<th scope=col>catégorie</th>')
for k, ebauche in ipairs( paramAdj ) do
wikiTab
:insert('<tr><td>')
:insert( ebauche.clef )
:insert('</td><td>')
:insert( ebauche.feminin )
:insert('</td><td>')
if ebauche.icone then
wikiTab
:insert( cfg.formatLien:format( ebauche.icone, '45x35', ebauche.altIcone or '' ) )
end
wikiTab
:insert('</td><td>')
:insert( ebauche.sujet )
:insert('</td><td>')
:insert( ebauche.sujetF or ebauche.sujet:gsub( ebauche.nom .. '%f[%W]', ebauche.feminin ) )
:insert('</td><td>')
if ebauche.categ then
wikiTab
:insert( '[[:Categoria:Wikipédia:ébauche ' .. ebauche.categ .. '|' .. ebauche.categ .. ']]' )
end
wikiTab
:insert('</td></tr>')
end
wikiTab:insert('</table>')
-- troisième table pour les types
wikiTab
:insert('\n=== Types ===\n')
:insert('<table class="wikitable">')
:insert('<caption>Liste des paramètres de type</caption>')
:insert('<th scope=col style="width:15%;">nom</th>')
:insert('<th scope=col style="width:50px;">icone</th>')
:insert('<th scope=col style="width:20%;">type</th>')
:insert('<th scope=col style="width:15%;">sujet</th>')
:insert('<th scope=col style="width:15%;">catégorie</th>')
:insert('<th scope=col>message</th>')
for k, ebauche in ipairs( paramType ) do
wikiTab
:insert('<tr><td>')
:insert( ebauche.clef )
:insert('</td><td>')
if ebauche.icone then
wikiTab:insert( cfg.formatLien:format( ebauche.icone, '45x35', ebauche.altIcone or '' ) )
end
wikiTab
:insert('</td><td>')
:insert ( ebauche.type )
:insert('</td><td>')
:insert( ebauche.sujet )
:insert('</td><td>')
if ebauche.categ then
wikiTab:insert( '[[:Categoria:Wikipédia:ébauche ' .. ebauche.categ .. '|' .. ebauche.categ .. ']]' )
end
wikiTab
:insert('</td><td>')
:insert( ebauche.message )
:insert('</td></tr>')
end
wikiTab:insert('</table>')
return table.concat( wikiTab ):gsub( ' <adjF?>', '' )
end
-- fonction destinée au gadget [[MediaWiki:Gadget-BandeauxEbauches.js]]
function p.listeEbauches( frame )
local paramEbauche = mw.loadData( moduleEbauche )
local liste = {}
for k in pairs( paramEbauche ) do
if k ~= "" then
table.insert( liste, k )
end
end
table.sort( liste )
return table.concat( liste, '\n' )
end
-- Insertion dans la table p des fonctions appelées par les
-- modèles à l'aide d'un adaptateur de fonction.
local function adaptateur(nomFonction)
return function (frame)
local args
if frame.args.texte or frame.args.titre then
args = frame.args
else
args = frame:getParent().args
end
return p[nomFonction](args)
end
end
local nomsFonction = {'bandeau', 'bandeauAvertissement', 'bandeauSection', 'ébauche' }
for _, nomFonction in ipairs(nomsFonction) do
p[nomFonction] = adaptateur('_' .. nomFonction)
end
return p