La documentazione per questo modulo può essere creata in Modulo:Navbox/man

--[[
* Modulo per implementare le funzionalità dei template Navbox e Navbox_subgroup.
* Costruisce un template di navigazione basato su una table HTML.
]]

require('Modulo:No globals')

local getArgs = require('Modulo:Arguments').getArgs

-- Configurazione
local cfg = mw.loadData('Modulo:Navbox/Configurazione')

-------------------------------------------------------------------------------
--                             Funzioni di utilità
-------------------------------------------------------------------------------

-- Ritorna true se il nome dell'argomento è valido
local function isValidArg(name, validArgs, maxList)
	local ret = validArgs[name] ~= nil

	if not ret then
		local id = name:match('^list(%d+)$') or name:match('^group(%d+)$') or
			name:match('^list(%d+)style$') or name:match('^group(%d+)style$')
		if id then
			ret = tonumber(id) <= maxList
		end
	end

	return ret
end

-- Ritorna gli argomenti passati al modulo, scartando quelli senza nome,
-- quelli contenenti stringhe vuote e i non riconosciuti.
local function parseArgs(args, isSubgroup)
	local ret = {}
	local validArgs = isSubgroup and cfg.subgroupArgs or cfg.navboxArgs
	local maxList = isSubgroup and cfg.subgroupMaxList or cfg.navboxMaxList

	for k, v in pairs(args) do
		if type(k) == 'string' and v ~= '' and isValidArg(k, validArgs, maxList) then
			ret[k] = v
		end
	end

	return ret
end

-- Ritorna una sequence contenente gli ID dei listN presenti, ordinata e senza duplicati.
-- Se withGroup è true, controlla anche i groupN.
local function getIds(args, withGroup)
	local ids = {}
	local ret = {}
 
	-- siccome Lua ha solo le table e non i set (elementi unici), prima popola ids usando le chiavi
	for k, _ in pairs(args) do
		local id = k:match('^list(%d+)$') or (withGroup and k:match('^group(%d+)$'))
		if id then
			ids[tonumber(id)] = 1
		end
	end

	-- quindi ritorna una sequence fatta delle chiavi della table ids
	for k, _ in pairs(ids) do
		table.insert(ret, k)
	end
	table.sort(ret)

	return ret
end

-- Toglie eventuali spazi/a capo dannosi attorno ai {{,}}
local function trimSep(list)
	local sep = mw.getCurrentFrame():expandTemplate{title=","}
	local sepEsc = mw.ustring.gsub(sep, '-', '%-')
	return mw.ustring.gsub(list, '%s*' .. sepEsc .. '%s*', sep)
end

-- Con il debug ridefinisce il metodo mw.html:css,
-- permettendo di eseguire i test senza controllare anche i css.
local function disableCSS(tableNode)
	local mt = getmetatable(tableNode)
	mt.__index.css = function(t, name, val) return t end
end

-------------------------------------------------------------------------------
--                           classe Navbox
-------------------------------------------------------------------------------

local Navbox = {}

function Navbox:new(args)
	local self = {}
	local thNode
	local thStyle = {
		['text-align'] = 'center',
		width = '100%',
		background = '#ccf',
		['font-size'] = '90%'
	}

	setmetatable(self, { __index = Navbox })
	self.args = args
	-- costruzione table
	self.tableNode = mw.html.create('table')
	if self.args.debug then
		disableCSS(self.tableNode)
	end
	self:_setupTableNode()
	-- prima row: contiene la navbar e il titolo
	thNode = self.tableNode:tag('tr')
		:tag('th')
			:attr('colspan', self.args.image and '3' or '2')
			:css(thStyle)
			:cssText(self.args.titlestyle)
	if self.args.navbar ~= 'plain' then
		self:_addTnavbar(thNode)
	end
	if self.args.title then
		self:_addTitle(thNode)
	end
	-- eventuale row per l'above
	if self.args.above then
		self:_addAboveOrBelow(self.args.above, self.args.abovestyle)
	end
	-- altre row
	self:_addLists()
	-- eventuale row finale per il below
	if self.args.below then
		self:_addAboveOrBelow(self.args.below, self.args.belowstyle)
	end

	return self
end

function Navbox:getHTML()
	return tostring(self.tableNode)
end

function Navbox:_setupTableNode()
	local tableStyle = {
		margin = 'auto',
		width = '100%',
		clear = 'both',
		border = '1px solid #aaa',
		padding = '2px'
	}
	self.tableNode
		:addClass('navbox')
		:addClass('mw-collapsible')
		:addClass(self.args.state == 'collapsed' and 'mw-collapsed' or
				  (self.args.state == 'autocollapse' and 'autocollapse' or
				  (not self.args.state and 'autocollapse' or nil)))
		:addClass('nowraplinks')
		:addClass('noprint')
		:addClass('metadata')
		:css(tableStyle)
		:cssText(self.args.style)
		:cssText(self.args.bodystyle)
end

function Navbox:_addTnavbar(node)
	local divStyle = {
		float = 'left',
		width = '6em',
		['text-align'] = 'left',
		padding = '0 10px 0 0',
		margin = '0px'
	}
	local tnavbar = mw.getCurrentFrame():expandTemplate {
		title = 'Tnavbar',
		args = {
			[1] = self.args.name,
			['mini'] = 1
		}
	}
	node:tag('div'):css(divStyle):wikitext(tnavbar)
end

function Navbox:_addTitle(node)
	node:tag('span'):css('font-size', '110%'):wikitext(self.args.title)
end

function Navbox:_addAboveOrBelow(arg, argStyle)
	local tdStyle = {
		background = '#ddf',
		['text-align'] = 'center',
		['font-size'] = '90%'
	}
	self.tableNode
		:tag('tr')
			:tag('td')
				:attr('colspan', self.args.image and '3' or '2')
				:css(tdStyle)
				:cssText(argStyle)
				:wikitext(arg)
end

function Navbox:_addImage(trNode, rowspan)
	local tdStyle = {
		['vertical-align'] = 'middle',
		['padding-left'] = '7px',
		width = '0%'
	}
	trNode
		:tag('td')
			:attr('rowspan', rowspan)
			:css(tdStyle)
			:cssText(self.args.imagestyle)
			:wikitext(self.args.image)
end

function Navbox:_addLists()
	local rowIds, altStyle, altBackground
	local thStyle = {
		background = '#ddf',
		['white-space'] = 'nowrap',
		padding = '0 10px',
		['font-size'] = '90%'
	}
	-- crea una riga per ogni groupN/listN
	rowIds = getIds(self.args, true)
	for _, id in ipairs(rowIds) do
		local trNode = self.tableNode:tag('tr')
		-- groupN
		if self.args['group' .. id] then
			trNode:tag('th')
				:attr('colspan', self.args['list' .. id] and '1' or '2')
				:css(thStyle)
				:cssText(self.args.groupstyle)
				:cssText(self.args['group' .. id .. 'style'])
				:wikitext(self.args['group' .. id])
		end
		-- listN
		if self.args['list' .. id] then
			local list = trimSep(self.args['list' .. id])
			if (id % 2) == 0 then
				altStyle = self.args.evenstyle
				altBackground = '#f7f7f7'
			else
				altStyle = self.args.oddstyle
				altBackground = nil
			end
			trNode:tag('td')
				:attr('colspan', self.args['group' .. id] and '1' or '2')
				:css('width', '100%')
				:css('font-size', '90%')
				:css('text-align', self.args['group' .. id] and 'left' or 'center')
				:css('background', altBackground)
				:cssText(self.args.liststyle)
				:cssText(altStyle)
				:cssText(self.args['list' .. id .. 'style'])
				:wikitext(list)
		end
		if id == 1 and self.args.image then
			self:_addImage(trNode, #rowIds)
		end
	end
end

-------------------------------------------------------------------------------
--                           classe NavboxSubgroup
-------------------------------------------------------------------------------

local NavboxSubgroup = {}

function NavboxSubgroup:new(args)
	local self = {}

	setmetatable(self, { __index = NavboxSubgroup })
	self.args = args
	-- costruzione table
	self.tableNode = mw.html.create('table')
	if self.args.debug then
		disableCSS(self.tableNode)
	end
	self:_setupTableNode()
	self:_addLists()

	return self
end

function NavboxSubgroup:getHTML()
	return tostring(self.tableNode)
end

function NavboxSubgroup:_setupTableNode()
	local tableStyle = {
		background = 'transparent',
		['font-size'] = '100%',
		padding = '0',
		border = '0',
		margin = '-3px',
		width = '100%'
	}
	self.tableNode
		:addClass('navbox')
		:addClass('nowraplinks')
		:css(tableStyle)
		:cssText(self.args.bodystyle)
end

function NavboxSubgroup:_addLists()
	local listIds, altStyle
	local thStyle = {
		background = '#ddf',
		padding = '0 10px',
	}
	-- crea una row per ogni listN
	listIds = getIds(self.args)
	for _, id in ipairs(listIds) do
		local trNode = self.tableNode:tag('tr')
		local list = trimSep(self.args['list' .. id])
		-- i groupN sono visibili solo se c'è la corrispettiva listN
		if self.args['group' .. id] then
			trNode:tag('th')
				:css(thStyle)
				:cssText(self.args.groupstyle)
				:wikitext(self.args['group' .. id])
		end
		if (id % 2) == 0 then
			altStyle = self.args.evenstyle
		else
			altStyle = self.args.oddstyle
		end
		trNode:tag('td')
			:attr('colspan', self.args['group' .. id] and '1' or '2')
			:css('text-align', self.args['group' .. id] and 'left' or 'center')
			:cssText(self.args.liststyle)
			:cssText(altStyle)
			:wikitext(list)
	end
end

-------------------------------------------------------------------------------
--                                    API
-------------------------------------------------------------------------------

local p = {}

function p._navbox(args)
	return Navbox:new(parseArgs(args)):getHTML()
end

function p._navbox_subgroup(args)
	return NavboxSubgroup:new(parseArgs(args, true)):getHTML()
end

-- Entry-point per {{Navbox}}
function p.navbox(frame)
	return p._navbox(getArgs(frame, {wrappers = 'Template:Navbox'}))
end

-- Entry-point per {{Navbox subgroup}}
function p.navbox_subgroup(frame)
	return p._navbox_subgroup(getArgs(frame, {wrappers = 'Template:Navbox subgroup'}))
end

return p