Aller au contenu

Module:ArgumentContent

De Wikidébats, l'encyclopédie des débats et des arguments « pour » et « contre »

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

--	Module:ArgumentContent (MediaWiki 1.43) — Optimisé (style Module:Debate)
--	Objectif : conserver le rendu, réduire le coût Lua, et remplacer #formlink/#queryformlink par Special:AddData / Special:RunQuery

local p = {}

----------------------------------------------------------------------
--	Accès global + micro-opt
----------------------------------------------------------------------

local mw				= mw
local F					= mw.getCurrentFrame()
local V					= ( mw.ext and mw.ext.VariablesLua ) or nil
local WD_I18N			= require( 'Module:WD/I18N' )

local tostring			= tostring
local type				= type
local tonumber			= tonumber
local ipairs			= ipairs
local pairs				= pairs

local table_concat		= table.concat
local string_format		= string.format
local string_lower		= string.lower

local uri_encode		= mw.uri.encode
local uri_decode		= mw.uri.decode
local uri_anchorEncode	= mw.uri.anchorEncode
local uri_localUrl		= mw.uri.localUrl

local t_trim			= mw.text.trim
local t_gsplit			= mw.text.gsplit
local t_nowiki			= mw.text.nowiki

local ITEM_SEP			= "⟭"
local FIELD_SEP			= "⟬"
local PATH_SEP			= "⟭"
local ROOT_SEP			= "⟬"

----------------------------------------------------------------------
--	table.insert optimisé (comme Module:Debate)
----------------------------------------------------------------------

local function table_insert( t, a, b )
	if b == nil then
		return table.insert( t, a )
	end

	if type( a ) ~= "number" then
		if type( b ) == "number" then
			return table.insert( t, a )
		end
		return table.insert( t, b )
	end

	return table.insert( t, a, b )
end

local function push( t, s )
	t[ #t + 1 ] = s
end

----------------------------------------------------------------------
--	VariablesLua wrappers (no-op si extension absente)
----------------------------------------------------------------------

local function vset( name, val )
	if V and type( V.vardefine ) == "function" then
		pcall( V.vardefine, name, val ~= nil and tostring( val ) or "" )
	end
end

local function vget( name )
	if V and type( V.var ) == "function" then
		local ok, res = pcall( V.var, name )
		if ok then return res end
	end
	return nil
end

----------------------------------------------------------------------
--	Helpers
----------------------------------------------------------------------

local function getArgs( frame )
	local parent = frame:getParent()
	local A = {}

	local function add( k, v )
		if v == nil then return end
		v = t_trim( tostring( v ) )
		if v ~= "" then
			A[ k ] = v
		end
	end

	if parent and parent.args then
		for k, v in pairs( parent.args ) do
			add( k, v )
		end
	end

	if frame.args then
		for k, v in pairs( frame.args ) do
			add( k, v )
		end
	end

	return A
end

local function toNumber( s, default )
	local n = tonumber( s or "" )
	return n or default or 0
end

local function wkSplit2( s, sep )
	s = tostring( s or "" )
	sep = tostring( sep or "" )
	if sep == "" then
		return s, ""
	end

	local i = s:find( sep, 1, true )
	if not i then
		return s, ""
	end

	return s:sub( 1, i - 1 ), s:sub( i + #sep )
end

local function escapeAttr( s )
	s = tostring( s or "" )
	return s
		:gsub( "[\r\n\t]", " " )
		:gsub( "&", "&" )
		:gsub( "<", "&lt;" )
		:gsub( ">", "&gt;" )
		:gsub( '"', "&quot;" )
end

----------------------------------------------------------------------
--	PageForms : remplacement #formlink / #queryformlink (comme Module:Debate)
----------------------------------------------------------------------

local function wkDbKey( page )
	local t = mw.title.new( page )
	if not t then
		return tostring( page or "" ):gsub( "%s", "_" )
	end
	return t.prefixedText:gsub( " ", "_" )
end

local function wkEncodeAddDataSegment( s )
	s = tostring( s or "" )
	s = s:gsub( "%%", "%%25" )
	s = s:gsub( "%?", "%%3F" )
	s = s:gsub( "#", "%%23" )
	s = s:gsub( "/", "%%2F" )
	return s
end

local function wkAddDataPath( formName, pageTitle )
	local form = wkEncodeAddDataSegment( wkDbKey( formName ) )
	local page = wkEncodeAddDataSegment( wkDbKey( pageTitle ) )
	return "Special:AddData/" .. form .. "/" .. page
end

local function addDataLink( formName, pageTitle, linktext, tooltip )
	formName	= tostring( formName or "" )
	pageTitle	= tostring( pageTitle or "" )
	linktext	= tostring( linktext or "" )
	tooltip		= tostring( tooltip or "" )

	if formName == "" or pageTitle == "" then
		return ""
	end

	if linktext == "" then
		linktext = "&nbsp;"
	end

	local target = wkAddDataPath( formName, pageTitle )
	local link = string_format( '[[%s|%s]]', target, linktext )

	if tooltip ~= "" then
		return string_format(
			'<span class="wk-adddata-link" data-wk-tooltip="%s">%s</span>',
			escapeAttr( tooltip ),
			link
		)
	end

	return string_format( '<span class="wk-adddata-link">%s</span>', link )
end

local function wkRunQueryPath( formName )
	local form = wkEncodeAddDataSegment( wkDbKey( formName ) )
	return "Special:RunQuery/" .. form
end

local function normalizeTooltipText( s )
	s = tostring( s or "" )

	--	Apostrophes
	s = s:gsub( "'", "’" )

	--	Guillemets droits → typographiques
	s = s:gsub( '"', '“' )

	return s
end

local function wkRunQueryHtmlTagButton( formName, label, tooltip, query )
	formName	= tostring( formName or "" )
	label		= tostring( label or "" )
	query		= ( type( query ) == "table" ) and query or {}
	tooltip		= tostring( tooltip or "" )
	
	tooltip = normalizeTooltipText( tooltip )

	if formName == "" then
		return ""
	end

	local title	= wkRunQueryPath( formName )
	local href	= tostring( uri_localUrl( title, query ) )

	local content = "+ " .. label

	return F:preprocess(
		'<htmltag tagname="a"'
		.. ' href	href="#"'
		.. '	data-href="' .. escapeAttr( href ) .. '"'
		.. '	class="wk-btn__a wk-js-nav"'
		.. ( tooltip ~= "" and ( ' title="' .. escapeAttr( tooltip ) .. '"' ) or "" )
		.. '>'
		.. content
		.. '</htmltag>'
	)
end

----------------------------------------------------------------------
--	SMW access (pf #show) + cache VariablesLua
----------------------------------------------------------------------

local LOCAL_CACHE = {}

local function cache_get( key )
	local v = LOCAL_CACHE[ key ]
	if v ~= nil then
		return v
	end

	local vv = vget( key )
	if vv ~= nil and vv ~= "" then
		LOCAL_CACHE[ key ] = vv
		return vv
	end

	return nil
end

local function cache_set( key, value )
	local v = value or ""
	LOCAL_CACHE[ key ] = v
	vset( key, v )
end

local function smwShow( frame, page, property )
	page = t_trim( tostring( page or "" ) )
	property = t_trim( tostring( property or "" ) )
	if page == "" or property == "" then
		return ""
	end

	local cacheKey = "WD:pf#show|" .. page .. "|" .. property
	local cached = cache_get( cacheKey )
	if cached ~= nil then
		return cached
	end

	local out = ""
	local ok, res = pcall( function()
		return frame:callParserFunction( "#show", { page, "?" .. property } )
	end )
	if ok then
		out = t_trim( tostring( res or "" ) )
	end

	cache_set( cacheKey, out )
	return out
end

----------------------------------------------------------------------
--	SMW batch ask : une seule requête (avec tolérance FR/EN) + caches
----------------------------------------------------------------------

local ARGDATA_CACHE = {}
local PROP_CAND_CACHE = {}

local function getPropCandidates( lang, key )
	local byLang = PROP_CAND_CACHE[ lang ]
	if not byLang then
		byLang = {}
		PROP_CAND_CACHE[ lang ] = byLang
	end

	local cached = byLang[ key ]
	if cached then
		return cached
	end

	local out, seen = {}, {}

	local function add( v )
		v = t_trim( tostring( v or "" ) )
		if v ~= "" and not seen[ v ] then
			seen[ v ] = true
			out[ #out + 1 ] = v
		end
	end

	add( WD_I18N.msg( "ArgumentContent", lang, key ) )
	add( WD_I18N.msg( "ArgumentContent", "fr", key ) )
	add( WD_I18N.msg( "ArgumentContent", "en", key ) )

	byLang[ key ] = out
	return out
end

local function pushPrintouts( q, props )
	for _, name in ipairs( props ) do
		q[ #q + 1 ] = "?" .. name
	end
end

local function normalizePageValue( v )
	v = t_trim( tostring( v or "" ) )
	v = v:gsub( "^%[%[:?(.-)%]%]$", "%1" )
	v = v:gsub( "^(.-)|.*$", "%1" )
	return t_trim( v )
end

local function firstFieldCandidates( r, candidates, isPage )
	for _, key in ipairs( candidates or {} ) do
		local v = r[ key ]
		if v ~= nil then
			if type( v ) == "table" then
				v = v[ 1 ]
			end
			v = t_trim( tostring( v or "" ) )
			if v ~= "" then
				if isPage then
					v = normalizePageValue( v )
				end
				return v
			end
		end
	end
	return ""
end

local function hasSMWAsk()
	return type( mw.smw ) == "table" and type( mw.smw.ask ) == "function"
end

local function smwAskSafe( q )
	if not hasSMWAsk() then
		return nil
	end
	local ok, res = pcall( function() return mw.smw.ask( q ) end )
	return ok and res or nil
end

local function smwGetArgumentData( pageTitle, lang )
	pageTitle = t_trim( tostring( pageTitle or "" ) )
	if pageTitle == "" then
		return {}
	end

	local cacheKey = "WD:argDataByPage|" .. pageTitle
	local cached = ARGDATA_CACHE[ cacheKey ]
	if cached then
		return cached
	end

	local data = {
		pageTitle			= pageTitle,
		idArgument			= "",
		contenuArgument		= "",
		pageDebatDetaille	= "",
		pairesJustifsRaw	= "",
		pairesObjRaw		= "",
		bandeauxJustifsCsv	= "",
		bandeauxObjsCsv		= "",
	}

	local P_ID			= getPropCandidates( lang, "argument_number_prop" )
	local P_CONTENT		= getPropCandidates( lang, "argument_content_prop" )
	local P_JUSTIFS		= getPropCandidates( lang, "justifs_list_prop" )
	local P_OBJS		= getPropCandidates( lang, "objs_list_prop" )
	local P_DETAILED	= getPropCandidates( lang, "detailed_debate_prop" )
	local P_WARN_J		= getPropCandidates( lang, "justifs_warn_prop" )
	local P_WARN_O		= getPropCandidates( lang, "objs_warn_prop" )

	local query = {
		"[[ " .. pageTitle .. " ]]",
		"mainlabel=page",
		"limit=1",
	}

	pushPrintouts( query, P_ID )
	pushPrintouts( query, P_CONTENT )
	pushPrintouts( query, P_JUSTIFS )
	pushPrintouts( query, P_OBJS )
	pushPrintouts( query, P_DETAILED )
	pushPrintouts( query, P_WARN_J )
	pushPrintouts( query, P_WARN_O )

	local res = smwAskSafe( query )
	local row = res and res[ 1 ] or nil
	if not row then
		ARGDATA_CACHE[ cacheKey ] = data
		return data
	end

	data.idArgument			= firstFieldCandidates( row, P_ID, false )
	data.contenuArgument	= firstFieldCandidates( row, P_CONTENT, false )
	data.pageDebatDetaille	= firstFieldCandidates( row, P_DETAILED, true )
	data.pairesJustifsRaw	= firstFieldCandidates( row, P_JUSTIFS, false )
	data.pairesObjRaw		= firstFieldCandidates( row, P_OBJS, false )
	data.bandeauxJustifsCsv	= firstFieldCandidates( row, P_WARN_J, false )
	data.bandeauxObjsCsv	= firstFieldCandidates( row, P_WARN_O, false )

	ARGDATA_CACHE[ cacheKey ] = data
	return data
end

----------------------------------------------------------------------
--	Bandeaux (mémoïsés + template cache)
----------------------------------------------------------------------

local BANDEAUX_CACHE = {}
local BANNER_TPL_CACHE = {}

local function splitCSV( s, sep )
	s = t_trim( tostring( s or "" ) )
	if s == "" then return {} end
	sep = sep or ","

	local out = {}
	for part in t_gsplit( s, sep, true ) do
		part = t_trim( part )
		if part ~= "" then
			table_insert( out, part )
		end
	end
	return out
end

local function expandBandeaux( frame, csv, lang )
	csv = t_trim( tostring( csv or "" ) )
	if csv == "" then
		return ""
	end

	local ck = "WD:bandeaux|" .. lang .. "|" .. csv
	local c = BANDEAUX_CACHE[ ck ]
	if c ~= nil then
		return c
	end

	local tplBanner = BANNER_TPL_CACHE[ lang ]
	if not tplBanner then
		tplBanner = WD_I18N.msg( "Argument.templates", lang, "banner_any" )
		BANNER_TPL_CACHE[ lang ] = tplBanner
	end

	local html = {}
	for _, item in ipairs( splitCSV( csv, "," ) ) do
		local ok, res = pcall( function()
			return frame:expandTemplate{ title = tplBanner, args = { item } }
		end )
		if ok and res and res ~= "" then
			html[ #html + 1 ] = res
		end
	end

	local out = table_concat( html, "&#32;" )
	BANDEAUX_CACHE[ ck ] = out
	return out
end

----------------------------------------------------------------------
--	Rendu principal
----------------------------------------------------------------------

function p.render( frame )
	local args = getArgs( frame )
	local lang = WD_I18N.getLangFromArgs( args )

	local function msg( key, ... )
		return WD_I18N.msg( "ArgumentContent", lang, key, ... )
	end

	local function file( key )
		return WD_I18N.msg( "Common.files", lang, key )
	end

	--	Messages / props (canon)
	local PROP_ARG_CONTENT		= msg( "argument_content_prop" )
	local TPL_ARG_CONTENT_EVAL	= msg( "argument_content_eval_tpl" )
	local PROP_ARGS_MAP			= msg( "argument_map_prop" )

	local TITLE_JUSTIFS			= msg( "justifs_title" )
	local TITLE_OBJS			= msg( "objs_title" )

	local NONE_JUS				= msg( "none_justifications_msg" )
	local NONE_OBJ				= msg( "none_objections_msg" )
	local NONE_CONTENT			= msg( "none_content_msg" )

	local EDIT					= msg( "edit_label" )
	local EDIT_J_TT				= msg( "edit_justifs_tt" )
	local EDIT_O_TT				= msg( "edit_objs_tt" )

	local ADD_ARG				= msg( "add_argument_label" )
	local ADD_OBJ				= msg( "add_objection_label" )

	--	Params
	local pageArgument		= uri_decode( t_trim( args.argument or "" ) )
	local cheminRaw			= t_trim( args.path or args.chemin or "" )
	local levelRaw			= toNumber( args.level or args.niveau, 0 )
	local niveau			= levelRaw + 1
	local typeArg			= t_trim( args.type or "" )
	local avertissements	= t_trim( args.warnings or args.avertissements or "" )
	local pageParam			= t_trim( args.page or "" )

	local editableRaw		= string_lower( t_trim( args.editable or "" ) )
	local isEditable		= ( editableRaw == "yes" or editableRaw == "oui" or editableRaw == "true" or editableRaw == "1" )

	--	Racine depuis chemin (gsplit C)
	local racine = ""
	if cheminRaw ~= "" and cheminRaw:find( PATH_SEP, 1, true ) then
		local last = ""
		for part in t_gsplit( cheminRaw, PATH_SEP, true ) do
			part = t_trim( part )
			if part ~= "" then
				last = part
			end
		end
		if last ~= "" and last:find( ROOT_SEP, 1, true ) then
			local _, r = wkSplit2( last, ROOT_SEP )
			racine = t_trim( tostring( r or "" ) )
		end
	end

	--	Pré-calculs attributs
	local nowikiPage	= t_nowiki( pageParam or "" )
	local nowikiRoot	= t_nowiki( racine or "" )
	local nowikiPath	= t_nowiki( cheminRaw or "" )
	local nowikiWarn	= t_nowiki( avertissements or "" )

	local function uencQ( s )
		return uri_encode( s or "", "QUERY" )
	end

	local function aenc( s )
		return uri_anchorEncode( s or "" )
	end

	--	SMW : une requête
	local data = smwGetArgumentData( pageArgument, lang )

	local pageTitle				= data.pageTitle or pageArgument
	local idArgument			= data.idArgument or ""
	local contenuArgument		= data.contenuArgument or ""
	local pageDebatDetaille		= data.pageDebatDetaille or ""
	local pairesJustifsRaw		= data.pairesJustifsRaw or ""
	local pairesObjRaw			= data.pairesObjRaw or ""
	local bandeauxJustifsCsv	= data.bandeauxJustifsCsv or ""
	local bandeauxObjsCsv		= data.bandeauxObjsCsv or ""

	--	Si aucun contenu
	if contenuArgument == "" and pairesJustifsRaw == "" and pairesObjRaw == "" and pageDebatDetaille == "" then
		return '<div class="aucun-contenu">' .. NONE_CONTENT .. '</div>__NOTOC__'
	end

	--	Bandeaux (argument)
	local avertHtml = ""
	if avertissements ~= "" then
		avertHtml = expandBandeaux( frame, avertissements, lang )
	end

	--	Wrapper éditable (inchangé)
	local preOpen, preClose = "", ""
	if isEditable and levelRaw == 1 then
		preOpen = string_format( '<div class="contenu-argument-%s">', typeArg or "" )
		preClose = '</div>'
	end

	local bodyHtml = {}
	push( bodyHtml, string_format( '<div class="contenu-argument">%s</div>', contenuArgument or "" ) )

	--	Débat détaillé
	if pageDebatDetaille ~= "" then
		local carteArgs = smwShow( frame, pageDebatDetaille, PROP_ARGS_MAP )
		push( bodyHtml,
			string_format(
				'<div class="carte-debat-detaille onglet-externe">'
					.. '<div class="titre-debat-detaille">%s</div>'
					.. '%s'
				.. '</div>',
				msg( "detailed_debate_title", pageDebatDetaille ),
				carteArgs or ""
			)
		)
	else
		------------------------------------------------------------------
		--	List builder (gsplit + split2) : évite split() Lua imbriqués
		------------------------------------------------------------------
		local function buildList( pairesRaw, sens, sensInverseOpt )
			pairesRaw = t_trim( tostring( pairesRaw or "" ) )
			if pairesRaw == "" then
				return ""
			end

			local out = {}
			local sensInverse = sensInverseOpt
			local typePour = ( typeArg == "pour" )
			local tpl = ( isEditable and PROP_ARG_CONTENT ) or TPL_ARG_CONTENT_EVAL

			local hasAny = false

			for brut in t_gsplit( pairesRaw, ITEM_SEP, true ) do
				brut = t_trim( brut )
				if brut ~= "" then
					local id, titre = wkSplit2( brut, FIELD_SEP )
					id = t_trim( id )
					titre = t_trim( titre )

					if titre == "" then
						titre = id
					end

					if id ~= "" or titre ~= "" then
						hasAny = true

						local titreEnc = uencQ( titre )
						local idEnc = uencQ( id )

						local t = sens
						if sensInverse then
							t = typePour and "contre" or "pour"
						end

						push( out,
							string_format(
								'<div class="argument">'
									.. '<div id="%s" class="argument-title level-%d level-sup"'
										.. ' data-template="%s"'
										.. ' data-page="%s"'
										.. ' data-argument="%s"'
										.. ' data-type="%s"'
										.. ' data-level="%d"'
										.. ' data-root="%s"'
										.. ' data-path="%s⟭%s⟬%s"'
										.. ' data-warnings="%s">'
										.. '[[File:%s|11px|link=|alt=|class=fleche-deroulante]]'
										.. '[[File:%s|17px|link=|alt=|class=pictogramme-h4 mw-no-invert]]'
										.. '<span class="argument-label">%s</span>'
									.. '</div>'
									.. '<div class="argument-wrapper"></div>'
								.. '</div>',
								aenc( titre ),
								niveau,
								tpl,
								nowikiPage,
								escapeAttr( idEnc ),
								t_nowiki( t ),
								niveau,
								nowikiRoot,
								nowikiPath,
								t_nowiki( t ),
								escapeAttr( titreEnc ),
								nowikiWarn,
								file( "expand" ),
								file( ( t == "pour" ) and "arg_pro" or "arg_con" ),
								t_nowiki( titre )
							)
						)
					end
				end
			end

			if not hasAny then
				return '<div class="aucun-argument">' .. ( sensInverse and NONE_OBJ or NONE_JUS ) .. '</div>'
			end

			return table_concat( out, "" )
		end

		--	Forms (noms venant i18n)
		local formJust		= msg( "justifications_form" )
		local formObj		= msg( "objections_form" )
		local formNew		= msg( "new_argument_title_field" )
		local formType		= msg( "form_type" )

		local modJust, modObj, btnJust, btnObj = "", "", "", ""

		if isEditable then
			--	Remplace #formlink : Special:AddData/Form/Page
			modJust = addDataLink( formJust, pageTitle, EDIT, EDIT_J_TT )
			modObj = addDataLink( formObj, pageTitle, EDIT, EDIT_O_TT )

			--	Remplace #queryformlink : Special:RunQuery/Form + query + _run
			local typeJust = msg( "justification_type" )
			local typeObj = msg( "objection_type" )

			local qJust = {}
			qJust[ formNew .. "[" .. formType .. "]" ] = ( typeJust or "" )
			qJust[ formNew .. "[ID]" ] = ( idArgument or "" )
			qJust[ "_run" ] = "1"

			local qObj = {}
			qObj[ formNew .. "[" .. formType .. "]" ] = ( typeObj or "" )
			qObj[ formNew .. "[ID]" ] = ( idArgument or "" )
			qObj[ "_run" ] = "1"

			btnJust = wkRunQueryHtmlTagButton(
				formNew,
				ADD_ARG,
				msg( "add_justif_tt", pageArgument or "" ),
				qJust
			)

			btnObj = wkRunQueryHtmlTagButton(
				formNew,
				ADD_OBJ,
				msg( "add_obj_tt", pageArgument or "" ),
				qObj
			)
		end

		--	Icons
		local iconJustKey = ( typeArg == "pour" ) and "arg_pro" or "arg_con"
		local iconObjKey = ( typeArg == "pour" ) and "arg_con" or "arg_pro"

		local iconJust = '[[File:' .. file( iconJustKey ) .. '|17px|link=|alt=|class=pictogramme-h3 mw-no-invert]]'
		local iconObj = '[[File:' .. file( iconObjKey ) .. '|17px|link=|alt=|class=pictogramme-h3 mw-no-invert]]'

		local gauche = string_format(
			'<div class="colonne-gauche">'
				.. '<div class="NavFramex %s">'
					.. '<div class="NavHead"><span class="titre-boite">%s%s</span>%s</div>'
					.. '<div class="NavContent">'
						.. '<div>%s</div>'
						.. '%s'
					.. '</div>'
					.. '%s'
				.. '</div>'
			.. '</div>',
			( typeArg == "pour" ) and "bordure-argument-pour" or "bordure-argument-contre",
			iconJust,
			TITLE_JUSTIFS,
			( isEditable and ( '<span class="modifier-section">' .. modJust .. '</span>' ) or "" ),
			expandBandeaux( frame, bandeauxJustifsCsv, lang ),
			( pairesJustifsRaw ~= "" and buildList( pairesJustifsRaw, typeArg, false ) or ( '<div class="aucun-argument">' .. NONE_JUS .. '</div>' ) ),
			( isEditable and ( '<div class="NavButton"><div class="bouton-ajouter wk-btn mw-ui-button navigation-not-searchable">' .. btnJust .. '</div></div>' ) or "" )
		)

		local droite = string_format(
			'<div class="colonne-droite">'
				.. '<div class="NavFramex %s">'
					.. '<div class="NavHead"><span class="titre-boite">%s%s</span>%s</div>'
					.. '<div class="NavContent">'
						.. '<div>%s</div>'
						.. '%s'
					.. '</div>'
					.. '%s'
				.. '</div>'
			.. '</div>',
			( typeArg == "pour" ) and "bordure-argument-contre" or "bordure-argument-pour",
			iconObj,
			TITLE_OBJS,
			( isEditable and ( '<span class="modifier-section">' .. modObj .. '</span>' ) or "" ),
			expandBandeaux( frame, bandeauxObjsCsv, lang ),
			( pairesObjRaw ~= "" and buildList( pairesObjRaw, typeArg, true ) or ( '<div class="aucun-argument">' .. NONE_OBJ .. '</div>' ) ),
			( isEditable and ( '<div class="NavButton"><div class="bouton-ajouter wk-btn mw-ui-button navigation-not-searchable">' .. btnObj .. '</div></div>' ) or "" )
		)

		push( bodyHtml, '<div class="colonnes">' )
		push( bodyHtml, gauche )
		push( bodyHtml, droite )
		push( bodyHtml, '</div>' )
	end

	local html = {}

	if avertHtml ~= "" then
		push( html, string_format( '<div>%s</div>', avertHtml ) )
	end

	push( html, preOpen )
	push( html, table_concat( bodyHtml, "" ) )
	push( html, preClose )
	push( html, "__NOTOC__" )

	return table_concat( html, "" )
end

return p