« Module:Infobox » : différence entre les versions

Kharmitch (discussion | contributions)
mAucun résumé des modifications
Balise : Révoqué
Kims (discussion | contributions)
Aucun résumé des modifications
 
(9 versions intermédiaires par 2 utilisateurs non affichées)
Ligne 51 : Ligne 51 :
return mw.html.create('hr'):addClass('separator')
return mw.html.create('hr'):addClass('separator')
end
end
--[[
VaulttecData — données de fallout-maps.com, disponibles pour TOUTE infobox.
Placé ici, et non dans Module:Infobox/Lieu, pour que n'importe quelle infobox
— créature, quête, objet, faction — en dispose sans rien recopier. Elle
déclare simplement :
{ type = 'vaulttec row', label = 'Repère' }
{ type = 'vaulttec image' }
Le paramètre lu est « fomapmkr » ; une infobox qui en nommerait un autre
passe parameter = 'autre nom'.
Trois précautions, chacune pour une raison précise :
1. Le test sur mw.ext.vaulttecData. Si l'extension n'est pas installée ou
  vient d'être désactivée, l'appel direct lèverait « attempt to index a nil
  value » et ferait disparaître l'infobox ENTIÈRE de toutes les pages
  concernées. Une intégration facultative ne doit jamais pouvoir faire ça.
2. Le retour nil, jamais la chaîne vide. buildrow et buildrow1col ne testent
  que la nullité ; une chaîne vide est vraie en Lua et produirait une ligne
  d'infobox vide sur chaque page sans marqueur.
3. Aucun message d'erreur. Un identifiant inconnu, un lieu masqué ou une
  carte injoignable ne rendent rien du tout : l'extension le signale par ses
  catégories de suivi, ce qui est l'affaire des mainteneurs, pas des
  lecteurs.
]]
-- Une seule interrogation par identifiant et par page. L'extension déduplique
-- déjà côté serveur, mais autant ne pas repasser par la frontière Lua/PHP pour
-- la ligne ET la vignette du même marqueur.
local vaulttecCache = {}
-- « data » est passé explicitement, jamais lu dans la variable de fichier :
-- c'est ce qui rend ces fonctions utilisables telles quelles comme « value »
-- d'un bloc, getValue leur passant exactement cette signature.
local function vaulttecId(data, part)
local parameter = (part and part.parameter) or 'fomapmkr'
local id = data[parameter]
if not id or id == '' then
return nil
end
if not (mw.ext and mw.ext.vaulttecData) then
return nil
end
return id
end
local function vaulttecRecord(data, part)
local id = vaulttecId(data, part)
if not id then
return nil
end
if vaulttecCache[id] == nil then
vaulttecCache[id] = mw.ext.vaulttecData.location(id) or false
end
local answer = vaulttecCache[id]
if not answer or answer.status ~= 'found' or not answer.data then
return nil
end
return answer.data
end
--[[
Le nom du marqueur, lié à sa page sur la carte.
Présenté comme n'importe quelle autre donnée de l'infobox — « Propriétaires »,
« Quêtes » — plutôt que noyé dans la vignette : c'est une information à part
entière, et un lecteur doit pouvoir la lire sans regarder l'image.
]]
function p.vaulttecname(data, part)
local record = vaulttecRecord(data, part)
if not record or record.name == '' then
return nil
end
-- mw.text.nowiki est obligatoire : « name » est du texte libre, venu d'une
-- base alimentée par des soumissions, et le retour de #invoke est réanalysé
-- comme wikitexte. Un nom contenant « ] » fermerait le lien ; un nom
-- contenant « {{ » deviendrait une transclusion.
return '[' .. record.url .. ' ' .. mw.text.nowiki(record.name) .. ']'
end
p.vaulttecName = p.vaulttecname
--[[
La vignette de carte, déjà mise en forme par l'extension.
]]
function p.vaulttecthumbnail(data, part)
local id = vaulttecId(data, part)
if not id then
return nil
end
local rendered = mw.ext.vaulttecData.thumbnail(id)
if rendered == '' then
return nil
end
return rendered
end
p.vaulttecThumbnail = p.vaulttecthumbnail


--[[
--[[
Ligne 199 : Ligne 308 :
local separator
local separator
if part.separator then
if part.separator then
separator = separator(part)
-- p.separator, et non « separator » : la variable locale vient d'être
-- déclarée nil, l'appeler levait « attempt to call a nil value » dès
-- qu'un bloc « images » demandait un séparateur.
separator = p.separator(part)
end
end
return mw.html.create('div')
return mw.html.create('div')
Ligne 274 : Ligne 386 :
-- format
-- format
local formattedvalue = mw.html.create('div')
local formattedvalue = mw.html.create('div')
:wikitext('\n' .. value .. '\n') -- Le '\n' est requis lorsque value est une liste commençant par '*' ou '#'
:wikitext('\n' .. value) -- Le '\n' est requis lorsque value est une liste commençant par '*' ou '#'
if part.hidden == true then
if part.hidden == true then
Ligne 321 : Ligne 433 :
end
end
--local style = part.style or {}
--style['text-align'] = style['text-align'] or 'center'
--style['color'] = style['color'] or '#000000'
--style['background-color'] = style['background-color'] or '#F9F9F9'
local rowI = mw.html.create('tr')
local rowI = mw.html.create('tr')
Ligne 331 : Ligne 438 :
local colspan = '2'
local colspan = '2'
styleI['padding'] = '1px'
styleI['padding'] = '1px'
cellI = mw.html.create('td')
local cellI = mw.html.create('td')
:attr({colspan = colspan})
:attr({colspan = colspan})
:attr({align = 'center'})
:attr({align = 'center'})
Ligne 340 : Ligne 447 :
styleT['background-color'] = 'transparent'
styleT['background-color'] = 'transparent'
styleT['width'] = '100%'
styleT['width'] = '100%'
tabC = mw.html.create('table')
local tabC = mw.html.create('table')
:attr({cellspacing = '0'})
:attr({cellspacing = '0'})
:addClass('navigation-not-searchable')
:addClass('navigation-not-searchable')
Ligne 365 : Ligne 472 :
local widthCell = '44%'
local widthCell = '44%'
local widthCenter
if center then
if center then
widthCenter = '28%'
widthCenter = '28%'
Ligne 535 : Ligne 643 :
end
end
p.buildRow1Col = p.buildrow1col
p.buildRow1Col = p.buildrow1col
--[[
Les deux blocs VaulttecData.
Ils délèguent aux constructeurs standards plutôt que de fabriquer leur propre
HTML : une ligne VaulttecData doit être exactement une ligne d'infobox, avec
la même structure et les mêmes classes que les autres.
]]
function p.buildvaulttecrow(part)
return p.buildrow({
type = 'row',
label = part.label or 'Repère',
class = part.class,
parameter = part.parameter,
value = p.vaulttecname
})
end
p.buildVaulttecRow = p.buildvaulttecrow
function p.buildvaulttecimage(part)
local class = 'avt-infobox-vaulttec'
if part.class then
class = class .. ' ' .. part.class
end
return p.buildrow1col({
type = 'row1col',
class = class,
parameter = part.parameter,
value = p.vaulttecthumbnail
})
end
p.buildVaulttecImage = p.buildvaulttecimage


function p.buildtable(part)
function p.buildtable(part)
Ligne 637 : Ligne 778 :
end
end
p.buildInvalidBlock = p.buildinvalidblock
p.buildInvalidBlock = p.buildinvalidblock
--function p.buildexternaltext(part)
-- local value = getValue(part.value)
-- if value and (type(value) == 'string') then
-- externaltext = externaltext .. value
-- end
--end
--p.buildExternalText = p.buildexternaltext


--[[
--[[
Ligne 670 : Ligne 801 :
['text'] = p.buildtext,
['text'] = p.buildtext,
['title'] = p.buildtitle,
['title'] = p.buildtitle,
['vaulttec image'] = p.buildvaulttecimage,
['vaulttec row'] = p.buildvaulttecrow,
}
}
if type(block) ~= 'table' or (not block.type) or (not blocktypes[block.type]) then
if type(block) ~= 'table' or (not block.type) or (not blocktypes[block.type]) then
Ligne 680 : Ligne 813 :
function p.build()
function p.build()
localdata = require('Module:Infobox/Localdata')
localdata = require('Module:Infobox/Localdata')
item = localdata.item
local item = localdata.item


-- chargement du module de paramétrage
-- chargement du module de paramétrage

Dernière version du 7 août 2026 à 10:55

Documentation du module

Licence pour Wikipédia

Le contenu de cette page a été entièrement ou partiellement copié sur Wikipédia, l'encyclopédie libre, et est donc sous licence Creative Commons Paternité-Partage des Conditions Initiales à l'Identique 3.0 non transposé. La version originale, son historique et ses auteurs peuvent être trouvés sur la page Wikipédia "Module:Infobox".

Ce module génère une infobox à partir d'une structure définie dans un sous-module.

build(frame)

[modifier le wikicode]

Fonction à utiliser depuis un modèle, elle ne prend qu'un paramètre direct, nom, le nom du sous-module où est définie la structure de l'infobox. Par exemple Modèle:Infobox Quête contient {{#invoke:Infobox|build|nom=Quête}}, indiquant qu'il faut utiliser Module:Infobox/Quête. Les paramètres passés au modèles sont disponibles dans le Module:Infobox/Localdata, qui peut être appelé depuis les sous-modules.

La structure de l'infobox est définie par une table contenue dans un sous-module, et dont le format est :

{
  parts = {} -- les différentes sous-parties de l'infobox
}

La partie parts contient elle-même des tables, ou des fonctions utilisant les variables localdata et item pour créer des tables. Pour une liste des types de données acceptés, et des conseils pratiques, voir Aide:Infobox en Lua.

Documentation transclues de Module:Infobox/doc.
local p = {}

--[[
	Chargement des informations des données passés au modèle et des variables globales
]]

-- données concernant les paramètres passés au modèle
local localdata = {}

-- données concernant la page où est affichée l'infobox
local page = {
	name = mw.title.getCurrentTitle().prefixedText,
	namespace =  mw.title.getCurrentTitle().namespace
}

local abb = require('Module:Abréviation')

-- l'objet principal à retourner
local infobox = mw.html.create('div')

local i18n = {
	['see doc'] = 'Documentation du modèle',
	['edit'] = 'modifier',
	['edit code'] = 'modifier le code',
	['edit item'] = 'modifier Wikidata',
	['tracking cat'] = "Page utilisant des données de Wikidata",
	['invalid block type'] = "Bloc de données invalide dans le module d'infobox",
	['default cat'] = "Maintenance des infobox",
}

--[[
	Outils
]]

local function getValue(value, part)
	if type(value) == 'string' then
		return localdata[value]
	elseif type(value) == 'function' then
		return value(localdata, part)
	elseif type(value) == 'table' then
        -- si plusieurs paramètres possibles (legacy de vieux code), prendre le premier non vide
		for i, j in pairs(value) do
			if localdata[j] then
				return localdata[j]
			end
		end
	end
end

function p.separator()
	return mw.html.create('hr'):addClass('separator')	
end

--[[
	VaulttecData — données de fallout-maps.com, disponibles pour TOUTE infobox.

	Placé ici, et non dans Module:Infobox/Lieu, pour que n'importe quelle infobox
	— créature, quête, objet, faction — en dispose sans rien recopier. Elle
	déclare simplement :

		{ type = 'vaulttec row', label = 'Repère' }
		{ type = 'vaulttec image' }

	Le paramètre lu est « fomapmkr » ; une infobox qui en nommerait un autre
	passe parameter = 'autre nom'.

	Trois précautions, chacune pour une raison précise :

	1. Le test sur mw.ext.vaulttecData. Si l'extension n'est pas installée ou
	   vient d'être désactivée, l'appel direct lèverait « attempt to index a nil
	   value » et ferait disparaître l'infobox ENTIÈRE de toutes les pages
	   concernées. Une intégration facultative ne doit jamais pouvoir faire ça.

	2. Le retour nil, jamais la chaîne vide. buildrow et buildrow1col ne testent
	   que la nullité ; une chaîne vide est vraie en Lua et produirait une ligne
	   d'infobox vide sur chaque page sans marqueur.

	3. Aucun message d'erreur. Un identifiant inconnu, un lieu masqué ou une
	   carte injoignable ne rendent rien du tout : l'extension le signale par ses
	   catégories de suivi, ce qui est l'affaire des mainteneurs, pas des
	   lecteurs.
]]

-- Une seule interrogation par identifiant et par page. L'extension déduplique
-- déjà côté serveur, mais autant ne pas repasser par la frontière Lua/PHP pour
-- la ligne ET la vignette du même marqueur.
local vaulttecCache = {}

-- « data » est passé explicitement, jamais lu dans la variable de fichier :
-- c'est ce qui rend ces fonctions utilisables telles quelles comme « value »
-- d'un bloc, getValue leur passant exactement cette signature.
local function vaulttecId(data, part)
	local parameter = (part and part.parameter) or 'fomapmkr'
	local id = data[parameter]
	if not id or id == '' then
		return nil
	end

	if not (mw.ext and mw.ext.vaulttecData) then
		return nil
	end

	return id
end

local function vaulttecRecord(data, part)
	local id = vaulttecId(data, part)
	if not id then
		return nil
	end

	if vaulttecCache[id] == nil then
		vaulttecCache[id] = mw.ext.vaulttecData.location(id) or false
	end

	local answer = vaulttecCache[id]
	if not answer or answer.status ~= 'found' or not answer.data then
		return nil
	end

	return answer.data
end

--[[
	Le nom du marqueur, lié à sa page sur la carte.

	Présenté comme n'importe quelle autre donnée de l'infobox — « Propriétaires »,
	« Quêtes » — plutôt que noyé dans la vignette : c'est une information à part
	entière, et un lecteur doit pouvoir la lire sans regarder l'image.
]]
function p.vaulttecname(data, part)
	local record = vaulttecRecord(data, part)
	if not record or record.name == '' then
		return nil
	end

	-- mw.text.nowiki est obligatoire : « name » est du texte libre, venu d'une
	-- base alimentée par des soumissions, et le retour de #invoke est réanalysé
	-- comme wikitexte. Un nom contenant « ] » fermerait le lien ; un nom
	-- contenant « {{ » deviendrait une transclusion.
	return '[' .. record.url .. ' ' .. mw.text.nowiki(record.name) .. ']'
end
p.vaulttecName = p.vaulttecname

--[[
	La vignette de carte, déjà mise en forme par l'extension.
]]
function p.vaulttecthumbnail(data, part)
	local id = vaulttecId(data, part)
	if not id then
		return nil
	end

	local rendered = mw.ext.vaulttecData.thumbnail(id)
	if rendered == '' then
		return nil
	end

	return rendered
end
p.vaulttecThumbnail = p.vaulttecthumbnail

--[[
	Construction des blocs
]]

function p.buildtitle(part)
	local text = getValue(part.value, part) or part.textdefaultvalue or mw.title.getCurrentTitle().text
	local subtext = getValue(part.subtitle, part) or part.subtitledefaultvalue
	
	text = mw.ustring.gsub(text, " [(][^()]*[)]$", "")
	
	if subtext and (subtext ~= text) then
		text = text .. '<br /><small>' .. subtext .. '</small>'
	end
	local class = 'avt-infobox-header'
	
    local style = {}
	if part.style then
		for i, j in pairs(part.style) do
			style[i] = j
		end
	end
	
	local icon = getValue(part.icon, part) or nil
	if icon then
		icon = tostring(mw.html.create('div')
			:addClass('avt-infobox-header-icon')
			:wikitext('[[Fichier:'..icon..'|30x30px|alt=|link=]]')
			:allDone())
	end
	
	local subhead = nil
	if part.subhead then
		local subheadText = abb.links({
			getValue(part.subhead.games, part),
			part.subhead.link,
			part.subhead.subject
		})
		if subheadText then
			subhead = tostring(mw.html.create('div')
				:addClass('avt-infobox-header-subhead')
				:wikitext(subheadText)
				:allDone())
		end
	end

	local title = mw.html.create('div')
		:addClass(class)
		:css(style)
		:node(icon)
		:node(subhead)
		:tag('div')
			:addClass('avt-infobox-header-title')
			:wikitext(text)
		:allDone()
	
	return title
end
p.buildTitle = p.buildtitle

function p.buildimages(part)
	local images = {}
	local upright, link, caption, alt
	if type(part.imageparameters) == 'string' then
		part.imageparameters = { part.imageparameters }
	end
	if not part.imageparameters then -- s'il n'y a pas de paramètre image, continuer, peut-être y a-t-il une image par défaut définie dans le module d'infobox
		part.imageparameters = {}
	end
	for j, k in ipairs(part.imageparameters) do
		table.insert(images, localdata[k])
	end

	-- Images par défaut
	if #images == 0 then
--		if part.maintenancecat then
--			addMaintenanceCat(part.maintenancecat, part.sortkey)
--		end
		if part.defaultimages then
			images = part.defaultimages
			if type(images) == 'string' then
				images = { images }
			end
			upright = part.defaultimageupright
			caption = part.defaultimagecaption
			link = part.defaultimagelink
			alt = part.defaultimagealt
			if not alt then
				alt = 'une illustration pour cette infobox serait bienvenue'
			end
		end
	end
	
	if #images == 0 then
		return nil
	end
	
	upright = upright or getValue(part.uprightparameter) or part.defaultupright or "1.2"
	link = link or getValue(part.linkparameter) or part.defaultlink
	caption = caption or getValue(part.captionparameter) or part.defaultcaption
	alt = alt or getValue(part.altparameter) or part.defaultalt
	
	if tonumber(upright) then
		upright = tostring( tonumber(upright) / #images )
	end

	local style = part.style or {}

	-- Partie image

	local imagesString = ''
	for i,image in pairs(images) do
		if image == '-' then
			return
		end
		imagesString = imagesString ..  '[[Fichier:' .. image .. '|frameless'
		if alt then
			imagesString = imagesString .. '|alt=' .. alt
		end
		if link then
			imagesString = imagesString .. '|link=' .. link
		end
		if upright then
			imagesString = imagesString .. '|upright=' .. upright
		elseif #images > 1 then
			imagesString = imagesString .. '|upright=' .. ( 1 / #images )
		end
		imagesString = imagesString .. ']]'
	end

	local image = mw.html.create('div')
		:addClass("avt-infobox-images")
		:css(style)
		:wikitext(imagesString)

	-- Partie légende
	local captionobj
	if caption then
		captionobj = mw.html.create('div')
			:wikitext(caption)
			:css(part.legendstyle or {})
			:addClass("avt-infobox-images-legend")
			:done()
	end
	
	-- séparateur
	local separator
	if part.separator then
		-- p.separator, et non « separator » : la variable locale vient d'être
		-- déclarée nil, l'appeler levait « attempt to call a nil value » dès
		-- qu'un bloc « images » demandait un séparateur.
		separator = p.separator(part)
	end
	return mw.html.create('div')
		:node(image)
		:node(captionobj)
		:node(separator)
		:done()
end
p.buildImages = p.buildimages

function p.buildtext(part)
	local class = part.class or ''
	local style = {
		['text-align'] = 'center',
		['font-weight'] = 'bold'
	}
	if part.style then
		for i, j in pairs(part.style) do
			style[i] = j
		end
	end
	local text = getValue(part.value, part) or part.defaultvalue
	if text == '-' then
		return
	end
	if not text then
		--addMaintenanceCat(part.maintenancecat, part.sortkey)
		return nil
	end
	local formattedtext = mw.html.create('p')
		:addClass(class)
		:css(style)
		:wikitext(text)
		:done()
	return formattedtext
end
p.buildText = p.buildtext

function p.buildrow(part)
	local class = part.class or ''
	local style = part.style or {}
	local valueClass = part.valueClass or ''
	local valueStyle = part.valueStyle or {}
	local value, number = getValue(part.value, part)
	
	if not value then
		value = part.defaultvalue
	end
	if value == nil then
		return nil
	end
	if not number then
		number = 0 -- == indéfini
	end

--	if not value then
--		if part.maintenancecat then
--			local maintenancecat = getValue(part.maintenancecat, part)
--			addMaintenanceCat(maintenancecat, part.sortkey)
--		end
--		return nil
--	end

	local label = part.label
	if number > 1 and part.plurallabel then
		label = part.plurallabel
	elseif number == 1 and part.singularlabel then
		label = part.singularlabel
	end
	if type(label) == 'function' then
			label = label(localdata, localdata.item)
	end

	-- format
	local formattedvalue = mw.html.create('div')
		:wikitext('\n' .. value) -- Le '\n' est requis lorsque value est une liste commençant par '*' ou '#'
		
	if part.hidden == true then
		formattedvalue
			:attr({class="NavContent", style="display: none; text-align: left;"})
		formattedvalue = mw.html.create('div')
			:attr({class="NavFrame", title="[Afficher]/[Masquer]", style="border: none; padding: 0;"})
			:node(formattedvalue)
	end
	formattedvalue =  mw.html.create('td')
			:node(formattedvalue)
			:addClass(valueClass)
			:css(valueStyle)
			:allDone()
	
	local formattedlabel
	if label then
		formattedlabel = mw.html.create('th')
			:attr('scope', 'row')
			:wikitext(label)
			:done()
	end
	local row = mw.html.create('tr')
		:addClass(class)
		:css(style)
		:node(formattedlabel)
		:node(formattedvalue)
		:done()
	
	return row
end
p.buildRow = p.buildrow

function p.buildsuccession(part)
	if not part.value then
		return nil
	end
	
	local values = part.value
	local before = getValue(values['before'], part)
	local center = getValue(values['center'], part)
	local after  = getValue(values['after'], part)
	
	if not before and not center and not after then
		return nil
	end
		
	local rowI = mw.html.create('tr')
	
	local styleI = {}
	local colspan = '2'
	styleI['padding'] = '1px'
	local cellI = mw.html.create('td')
			:attr({colspan = colspan})
			:attr({align = 'center'})
			:css(styleI)
	
	local styleT = {}
	styleT['margin'] = '0px'
	styleT['background-color'] = 'transparent'
	styleT['width'] = '100%'
	local tabC = mw.html.create('table')
			:attr({cellspacing = '0'})
			:addClass('navigation-not-searchable')
			:css(styleT)
	
	local row = mw.html.create('tr')

	local color = part.color

	local style = {}
	local arrowLeft
	local arrowRight
	
	if color == 'default' then
		arrowLeft = '[[Fichier:SCRWEST.png|13px|alt=Précédent|link=]]'
		arrowRight = '[[Fichier:SCREAST.png|13px|alt=Suivant|link=]]'
	else
		arrowLeft = '[[Fichier:SCRWEST.png|13px|alt=Précédent|link=]]'
		arrowRight = '[[Fichier:SCREAST.png|13px|alt=Suivant|link=]]'
		style['background-color'] = color
	end
	
	local styleTrans = {}
	
	local widthCell = '44%'
	local widthCenter
	if center then
		widthCenter = '28%'
		widthCell = '29%'
	end
	
	local formattedbefore
	if before then
		formattedbefore = mw.html.create('td')
			:attr({valign = 'middle'})
			:attr({align = 'left'})
			:attr({width = '5%'})
			:css(style)
			:wikitext(arrowLeft)
			:done()
		row:node(formattedbefore)
		formattedbefore = mw.html.create('td')
			:attr({width = '1%'})
			:css(style)
			:wikitext('')
			:done()
		row:node(formattedbefore)
		formattedbefore = mw.html.create('td')
			:attr({align = 'left'})
			:attr({valign = 'middle'})
			:attr({width = widthCell})
			:css(style)
			:wikitext(before)
			:done()
		row:node(formattedbefore)
	else
		formattedbefore = mw.html.create('td')
			:attr({valign = 'middle'})
			:attr({align = 'left'})
			:attr({width = '5%'})
			:css(styleTrans)
			:wikitext('')
			:done()
		row:node(formattedbefore)
		formattedbefore = mw.html.create('td')
			:attr({width = '1%'})
			:css(styleTrans)
			:wikitext('')
			:done()
		row:node(formattedbefore)
		formattedbefore = mw.html.create('td')
			:attr({align = 'left'})
			:attr({valign = 'middle'})
			:attr({width = widthCell})
			:css(styleTrans)
			:wikitext('')
			:done()
		row:node(formattedbefore)
	end
	
	local formattedcenter
	formattedcenter = mw.html.create('td')
		:attr({width = '1%'})
		:css(styleTrans)
		:wikitext('')
		:done()
	row:node(formattedcenter)
	
	if center then
		formattedcenter = mw.html.create('td')
			:attr({align = 'center'})
			:attr({valign = 'middle'})
			:attr({width = widthCenter})
			:css(style)
			:wikitext(center)
			:done()
		row:node(formattedcenter)
		formattedcenter = mw.html.create('td')
			:attr({width = '1%'})
			:css(styleTrans)
			:wikitext('')
			:done()
		row:node(formattedcenter)
	end
	
	local formattedafter
	if after then
		formattedafter = mw.html.create('td')
			:attr({align = 'right'})
			:attr({valign = 'middle'})
			:attr({width = widthCell})
			:css(style)
			:wikitext(after)
			:done()
		row:node(formattedafter)
		formattedbefore = mw.html.create('td')
			:attr({width = '1%'})
			:css(style)
			:wikitext('')
			:done()
		row:node(formattedbefore)
		formattedafter = mw.html.create('td')
			:attr({align = 'right'})
			:attr({valign = 'middle'})
			:attr({width = '5%'})
			:css(style)
			:wikitext(arrowRight)
			:done()
		row:node(formattedafter)
	else
		formattedafter = mw.html.create('td')
			:attr({align = 'right'})
			:attr({valign = 'middle'})
			:attr({width = widthCell})
			:css(styleTrans)
			:wikitext('')
			:done()
		row:node(formattedafter)
		formattedbefore = mw.html.create('td')
			:attr({width = '1%'})
			:css(styleTrans)
			:wikitext('')
			:done()
		row:node(formattedbefore)
		formattedafter = mw.html.create('td')
			:attr({align = 'right'})
			:attr({valign = 'middle'})
			:attr({width = '5%'})
			:css(styleTrans)
			:wikitext('')
			:done()
		row:node(formattedafter)
	end
	
	row:done()
	tabC:node(row)
	tabC:done()
	cellI:node(tabC)
	cellI:done()
	rowI:node(cellI)
	rowI:allDone()
	
	return rowI
end
p.buildSuccession = p.buildsuccession

function p.buildrow1col(part)
	if not part.value then
		return nil
	end

	local class = 'row1col'
	if part.class then
		class = class .. ' ' .. part.class
	end
	
	local text = getValue(part.value, part)
	
	if not text then
		return nil
	end

	local formattedlabel
	formattedlabel = mw.html.create('th')
		:attr({ colspan = '2' })
		:addClass(class)
		:wikitext(text)
		:done()

	local row = mw.html.create('tr')
		:node(formattedlabel)
		:done()
	
	return row
end
p.buildRow1Col = p.buildrow1col

--[[
	Les deux blocs VaulttecData.

	Ils délèguent aux constructeurs standards plutôt que de fabriquer leur propre
	HTML : une ligne VaulttecData doit être exactement une ligne d'infobox, avec
	la même structure et les mêmes classes que les autres.
]]
function p.buildvaulttecrow(part)
	return p.buildrow({
		type = 'row',
		label = part.label or 'Repère',
		class = part.class,
		parameter = part.parameter,
		value = p.vaulttecname
	})
end
p.buildVaulttecRow = p.buildvaulttecrow

function p.buildvaulttecimage(part)
	local class = 'avt-infobox-vaulttec'
	if part.class then
		class = class .. ' ' .. part.class
	end

	return p.buildrow1col({
		type = 'row1col',
		class = class,
		parameter = part.parameter,
		value = p.vaulttecthumbnail
	})
end
p.buildVaulttecImage = p.buildvaulttecimage

function p.buildtable(part)
	local class = nil
	if part.collapseparameters then
		if part.collapseparameters.collapsible == true then
			class = 'mw-collapsible'
			if part.collapseparameters.collapsed == true then
				class = class ..' mw-collapsed'
			end
		end
	end
	
	local tab = mw.html.create('table')
		:addClass(class)
		:css(part.style or {})

	local rows = part.rows
	
	-- expand parameters so that we have a list of tables
	local i = 1

	while (i <= #rows) do 
		local l = rows[i]
		if type(l) == 'function' then 
			l = l(localdata, localdata.item)
		end
		if (type(l) == 'table') and (l.type == 'multi') then
			table.remove(rows, i)  
			for j, row in ipairs(l.rows) do
				table.insert(rows, i + j - 1, row) 
			end
		elseif type(l) == 'nil' then
			table.remove(rows, i)
		elseif type(l) ~= 'table' then 
			return error('les lignes d\'infobox ("rows") doivent être des tables, est ' .. type(l))
		else
			i = i + 1
		end
	end 

	-- CREATE ROW
	local expandedrows = {}
	for k, row in ipairs(rows) do
		local v = p.buildblock(row)
		if v then
			table.insert(expandedrows, v)
		end
	end
	if (#expandedrows == 0) then
		return nil
	end
	rows = expandedrows

	-- ADD TITLE
	local title
	if part.title or part.singulartitle or part.pluraltitle then
		local text
		if #rows > 1 and part.pluraltitle then
			text = part.pluraltitle
		elseif #rows == 1 and part.singulartitle then
			text = part.singulartitle
		else
			text = part.title
		end

		local style = part.titlestyle or {}

		title = mw.html.create('caption')
			:attr({ colspan = '2' })
			:css(style)
			:wikitext(text)
			:done()
	end
	
	if title then
		tab:node(title)
	end
	
	for i, j in pairs (rows) do
		tab:node(j)
	end
	
	if part.separator then
		local separator = p.separator(part)
		tab:node(separator)
	end
	tab:allDone()
	return tab
end
p.buildTable = p.buildtable

function p.buildinvalidblock(args)
--	addMaintenanceCat(defaultcat)
	local text = ''
	if type(args) ~= 'table' then
		text = "Les blocs d'infobox doivent être des tables"
	else
		text = i18n["invalid block type"] .. ' : ' .. (args.type or '??')
	end
	return text
end
p.buildInvalidBlock = p.buildinvalidblock

--[[
	Construction de l'infobox
]]

function p.buildblock(block)
	if type(block) == 'function' then
		block = block(localdata)
	end

	 -- list of functions for block buildings
	local blocktypes = {
		['invalid'] = p.buildinvalidblock,
--		['external text'] = p.buildexternaltext,
		['images'] = p.buildimages,
		['mixed'] = p.buildrow,
--		['navbox'] = p.buildnavbox,
		['table'] = p.buildtable,
		['row'] = p.buildrow,
		['row1col'] = p.buildrow1col,
		['succession'] = p.buildsuccession,
		['text'] = p.buildtext,
		['title'] = p.buildtitle,
		['vaulttec image'] = p.buildvaulttecimage,
		['vaulttec row'] = p.buildvaulttecrow,
	}
	if type(block) ~= 'table' or (not block.type) or (not blocktypes[block.type]) then
		return blocktypes['invalid'](block)
	end
	return blocktypes[block.type](block) 
end
p.buildBlock = p.buildblock

function p.build()
	localdata = require('Module:Infobox/Localdata')
	local item = localdata.item

	-- chargement du module de paramétrage
	local moduledata = require('Module:Infobox/' .. localdata.modulename)
	moduledata.name = localdata.modulename
	
	-- class
	local class = 'infobox avt-infobox'
	if moduledata.class then
		class = class .. ' ' .. moduledata.class
	end

	-- style
	local style = moduledata.style or {}
	
	-- build infobox
	infobox	:addClass(class)
			:css(style)
	
	for i, j in pairs(moduledata.parts) do
		infobox:node(p.buildblock(j))
	end

	return tostring(infobox)
end

return p