Differenze tra le versioni di "Modulo:Navbox"

Da Destiny One.
m (una versione importata)
(non è efficiente iterare su tutta la table per ogni parametro listN/groupN del modulo, fix ripetizione funzione getListIds/getIds)
(Una versione intermedia di un altro utente non mostrate)
Riga 1: Riga 1:
--
+
--[[
-- This module implements {{Navbox}}
+
* Modulo per implementare le funzionalità dei template Navbox e Navbox_subgroup.
--
+
* Costruisce un template di navigazione basato su una table HTML.
 +
]]
  
local p = {}
+
require('Modulo:No globals')
  
local navbar = require('Module:Navbar')._navbar
+
local getArgs = require('Modulo:Arguments').getArgs
local getArgs -- lazily initialized
 
  
local args
+
-- Configurazione
local tableRowAdded = false
+
local cfg = mw.loadData('Modulo:Navbox/Configurazione')
local border
 
local listnums = {}
 
  
local function trim(s)
+
-------------------------------------------------------------------------------
    return (mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1"))
+
--                            Funzioni di utilità
end
+
-------------------------------------------------------------------------------
  
local function addNewline(s)
+
-- Ritorna true se il nome dell'argomento è valido
    if s:match('^[*:;#]') or s:match('^{|') then
+
local function isValidArg(name, validArgs, maxList)
        return '\n' .. s ..'\n'
+
local ret = validArgs[name] ~= nil
    else
 
        return s
 
    end
 
end
 
  
local function addTableRow(tbl)
+
if not ret then
    -- If any other rows have already been added, then we add a 2px gutter row.
+
local id = name:match('^list(%d+)$') or name:match('^group(%d+)$') or
    if tableRowAdded then
+
name:match('^list(%d+)style$') or name:match('^group(%d+)style$')
        tbl
+
if id then
            :tag('tr')
+
ret = tonumber(id) <= maxList
                :css('height', '2px')
+
end
                :tag('td')
+
end
                    :attr('colspan',2)
 
    end
 
  
    tableRowAdded = true
+
return ret
 
 
    return tbl:tag('tr')
 
 
end
 
end
  
local function renderNavBar(titleCell)
+
-- Ritorna gli argomenti passati al modulo, scartando quelli senza nome,
    -- Depending on the presence of the navbar and/or show/hide link, we may need to add a spacer div on the left
+
-- quelli contenenti stringhe vuote e i non riconosciuti.
    -- or right to keep the title centered.
+
local function parseArgs(args, isSubgroup)
    local spacerSide = nil
+
local ret = {}
 
+
local validArgs = isSubgroup and cfg.subgroupArgs or cfg.navboxArgs
    if args.navbar == 'off' then
+
local maxList = isSubgroup and cfg.subgroupMaxList or cfg.navboxMaxList
        -- No navbar, and client wants no spacer, i.e. wants the title to be shifted to the left. If there's
 
        -- also no show/hide link, then we need a spacer on the right to achieve the left shift.
 
        if args.state == 'plain' then spacerSide = 'right' end
 
    elseif args.navbar == 'plain' or (not args.name and mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', '') == 'Template:Navbox') then
 
        -- No navbar. Need a spacer on the left to balance out the width of the show/hide link.
 
        if args.state ~= 'plain' then spacerSide = 'left' end
 
    else
 
        -- Will render navbar (or error message). If there's no show/hide link, need a spacer on the right
 
        -- to balance out the width of the navbar.
 
        if args.state == 'plain' then spacerSide = 'right' end
 
  
        titleCell:wikitext(navbar{
+
for k, v in pairs(args) do
            args.name,
+
if type(k) == 'string' and v ~= '' and isValidArg(k, validArgs, maxList) then
            mini = 1,
+
ret[k] = v
            fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') ..  ';background:none transparent;border:none;'
+
end
        })
+
end
    end
 
  
    -- Render the spacer div.
+
return ret
    if spacerSide then
 
        titleCell
 
            :tag('span')
 
                :css('float', spacerSide)
 
                :css('width', '6em')
 
                :wikitext('&nbsp;')
 
    end
 
 
end
 
end
  
--
+
-- Ritorna una sequence contenente gli ID dei listN presenti, ordinata e senza duplicati.
--   Title row
+
-- Se withGroup è true, controlla anche i groupN.
--
+
local function getIds(args, withGroup)
local function renderTitleRow(tbl)
+
local ids = {}
    if not args.title then return end
+
local ret = {}
 
+
    local titleRow = addTableRow(tbl)
+
-- 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
  
    if args.titlegroup then
+
-- quindi ritorna una sequence fatta delle chiavi della table ids
        titleRow
+
for k, _ in pairs(ids) do
            :tag('th')
+
table.insert(ret, k)
                :attr('scope', 'row')
+
end
                :addClass('navbox-group')
+
table.sort(ret)
                :addClass(args.titlegroupclass)
 
                :cssText(args.basestyle)
 
                :cssText(args.groupstyle)
 
                :cssText(args.titlegroupstyle)
 
                :wikitext(args.titlegroup)
 
    end
 
  
    local titleCell = titleRow:tag('th'):attr('scope', 'col')
+
return ret
 
 
    if args.titlegroup then
 
        titleCell
 
            :css('border-left', '2px solid #fdfdfd')
 
            :css('width', '100%')
 
    end
 
 
 
    local titleColspan = 2
 
    if args.imageleft then titleColspan = titleColspan + 1 end
 
    if args.image then titleColspan = titleColspan + 1 end
 
    if args.titlegroup then titleColspan = titleColspan - 1 end
 
 
 
    titleCell
 
        :cssText(args.basestyle)
 
        :cssText(args.titlestyle)
 
        :addClass('navbox-title')
 
        :attr('colspan', titleColspan)
 
 
 
    renderNavBar(titleCell)
 
 
 
    titleCell
 
        :tag('div')
 
            :attr('id', mw.uri.anchorEncode(args.title))
 
            :addClass(args.titleclass)
 
            :css('font-size', '114%')
 
            :wikitext(addNewline(args.title))
 
 
end
 
end
  
--
+
-- Toglie eventuali spazi/a capo dannosi attorno ai {{,}}
--  Above/Below rows
+
local function trimSep(list)
--
+
local sep = mw.getCurrentFrame():expandTemplate{title=","}
 
+
local sepEsc = mw.ustring.gsub(sep, '-', '%-')
local function getAboveBelowColspan()
+
return mw.ustring.gsub(list, '%s*' .. sepEsc .. '%s*', sep)
    local ret = 2
 
    if args.imageleft then ret = ret + 1 end
 
    if args.image then ret = ret + 1 end
 
    return ret
 
 
end
 
end
  
local function renderAboveRow(tbl)
+
-- Con il debug ridefinisce il metodo mw.html:css,
    if not args.above then return end
+
-- permettendo di eseguire i test senza controllare anche i css.
 
+
local function disableCSS(tableNode)
    addTableRow(tbl)
+
local mt = getmetatable(tableNode)
        :tag('td')
+
mt.__index.css = function(t, name, val) return t end
            :addClass('navbox-abovebelow')
 
            :addClass(args.aboveclass)
 
            :cssText(args.basestyle)
 
            :cssText(args.abovestyle)
 
            :attr('colspan', getAboveBelowColspan())
 
            :tag('div')
 
                :wikitext(addNewline(args.above))
 
 
end
 
end
  
local function renderBelowRow(tbl)
+
-------------------------------------------------------------------------------
    if not args.below then return end
+
--                          classe Navbox
 +
-------------------------------------------------------------------------------
  
    addTableRow(tbl)
+
local Navbox = {}
        :tag('td')
 
            :addClass('navbox-abovebelow')
 
            :addClass(args.belowclass)
 
            :cssText(args.basestyle)
 
            :cssText(args.belowstyle)
 
            :attr('colspan', getAboveBelowColspan())
 
            :tag('div')
 
                :wikitext(addNewline(args.below))
 
end
 
  
--
+
function Navbox:new(args)
--  List rows
+
local self = {}
--
+
local thNode
local function renderListRow(tbl, listnum)
+
local thStyle = {
    local row = addTableRow(tbl)
+
['text-align'] = 'center',
 +
width = '100%',
 +
background = '#ccf',
 +
['font-size'] = '90%'
 +
}
  
    if listnum == 1 and args.imageleft then
+
setmetatable(self, { __index = Navbox })
        row
+
self.args = args
            :tag('td')
+
-- costruzione table
                :addClass('navbox-image')
+
self.tableNode = mw.html.create('table')
                :addClass(args.imageclass)
+
if self.args.debug then
                :css('width', '0%')
+
disableCSS(self.tableNode)
                :css('padding', '0px 2px 0px 0px')
+
end
                :cssText(args.imageleftstyle)
+
self:_setupTableNode()
                :attr('rowspan', 2 * #listnums - 1)
+
-- prima row: contiene la navbar e il titolo
                :tag('div')
+
thNode = self.tableNode:tag('tr')
                    :wikitext(addNewline(args.imageleft))
+
:tag('th')
    end
+
: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
  
    if args['group' .. listnum] then
+
return self
        local groupCell = row:tag('th')
+
end
  
        groupCell
+
function Navbox:getHTML()
            :attr('scope', 'row')
+
return tostring(self.tableNode)
            :addClass('navbox-group')
+
end
            :addClass(args.groupclass)
 
            :cssText(args.basestyle)
 
 
 
        if args.groupwidth then
 
            groupCell:css('width', args.groupwidth)
 
        end
 
 
 
        groupCell
 
            :cssText(args.groupstyle)
 
            :cssText(args['group' .. listnum .. 'style'])
 
            :wikitext(args['group' .. listnum])
 
    end
 
  
    local listCell = row:tag('td')
+
function Navbox:_setupTableNode()
 
+
local tableStyle = {
    if args['group' .. listnum] then
+
margin = 'auto',
        listCell
+
width = '100%',
            :css('text-align', 'left')
+
clear = 'both',
            :css('border-left-width', '2px')
+
border = '1px solid #aaa',
            :css('border-left-style', 'solid')
+
padding = '2px'
    else
+
}
        listCell:attr('colspan', 2)
+
self.tableNode
    end
+
:addClass('navbox')
 
+
:addClass('mw-collapsible')
    if not args.groupwidth then
+
:addClass(self.args.state == 'collapsed' and 'mw-collapsed' or
        listCell:css('width', '100%')
+
  (self.args.state == 'autocollapse' and 'autocollapse' or
    end
+
  (not self.args.state and 'autocollapse' or nil)))
 
+
:addClass('nowraplinks')
    local isOdd = (listnum % 2) == 1
+
:addClass('noprint')
    local rowstyle = args.evenstyle
+
:addClass('metadata')
    if isOdd then rowstyle = args.oddstyle end
+
:css(tableStyle)
 
+
:cssText(self.args.style)
    local evenOdd
+
:cssText(self.args.bodystyle)
    if args.evenodd == 'swap' then
 
        if isOdd then evenOdd = 'even' else evenOdd = 'odd' end
 
    else
 
        if isOdd then evenOdd = args.evenodd or 'odd' else evenOdd = args.evenodd or 'even' end
 
    end
 
 
 
    listCell
 
        :css('padding', '0px')
 
        :cssText(args.liststyle)
 
        :cssText(rowstyle)
 
        :cssText(args['list' .. listnum .. 'style'])
 
        :addClass('navbox-list')
 
        :addClass('navbox-' .. evenOdd)
 
        :addClass(args.listclass)
 
        :tag('div')
 
            :css('padding', (listnum == 1 and args.list1padding) or args.listpadding or '0em 0.25em')
 
            :wikitext(addNewline(args['list' .. listnum]))
 
 
 
    if listnum == 1 and args.image then
 
        row
 
            :tag('td')
 
                :addClass('navbox-image')
 
                :addClass(args.imageclass)
 
                :css('width', '0%')
 
                :css('padding', '0px 0px 0px 2px')
 
                :cssText(args.imagestyle)
 
                :attr('rowspan', 2 * #listnums - 1)
 
                :tag('div')
 
                    :wikitext(addNewline(args.image))
 
    end
 
 
end
 
end
  
 
+
function Navbox:_addTnavbar(node)
--
+
local divStyle = {
--  Tracking categories
+
float = 'left',
--
+
width = '6em',
 
+
['text-align'] = 'left',
local function needsHorizontalLists()
+
padding = '0 10px 0 0',
    if border == 'child' or border == 'subgroup'  or args.tracking == 'no' then return false end
+
margin = '0px'
 
+
}
    local listClasses = {'plainlist', 'hlist', 'hlist hnum', 'hlist hwrap', 'hlist vcard', 'vcard hlist', 'hlist vevent'}
+
local tnavbar = mw.getCurrentFrame():expandTemplate {
    for i, cls in ipairs(listClasses) do
+
title = 'Tnavbar',
        if args.listclass == cls or args.bodyclass == cls then
+
args = {
            return false
+
[1] = self.args.name,
        end
+
['mini'] = 1
    end
+
}
 
+
}
    return true
+
node:tag('div'):css(divStyle):wikitext(tnavbar)
 
end
 
end
  
local function hasBackgroundColors()
+
function Navbox:_addTitle(node)
    return mw.ustring.match(args.titlestyle or '','background') or mw.ustring.match(args.groupstyle or '','background') or mw.ustring.match(args.basestyle or '','background')
+
node:tag('span'):css('font-size', '110%'):wikitext(self.args.title)
 
end
 
end
  
local function isIllegible()
+
function Navbox:_addAboveOrBelow(arg, argStyle)
    local styleratio = require('Module:Color contrast')._styleratio
+
local tdStyle = {
 
+
background = '#ddf',
    for key, style in pairs(args) do
+
['text-align'] = 'center',
        if tostring(key):match("style$") then
+
['font-size'] = '90%'
            if styleratio{mw.text.unstripNoWiki(style)} < 4.5 then
+
}
                return true
+
self.tableNode
            end
+
:tag('tr')
        end
+
:tag('td')
    end
+
:attr('colspan', self.args.image and '3' or '2')
    return false
+
:css(tdStyle)
 +
:cssText(argStyle)
 +
:wikitext(arg)
 
end
 
end
  
local function getTrackingCategories()
+
function Navbox:_addImage(trNode, rowspan)
    local cats = {}
+
local tdStyle = {
    if needsHorizontalLists() then table.insert(cats, 'Navigational boxes without horizontal lists') end
+
['vertical-align'] = 'middle',
    if hasBackgroundColors() then table.insert(cats, 'Navboxes using background colours') end
+
['padding-left'] = '7px',
    if isIllegible() then table.insert(cats, 'Potentially illegible navboxes') end
+
width = '0%'
    return cats
+
}
 +
trNode
 +
:tag('td')
 +
:attr('rowspan', rowspan)
 +
:css(tdStyle)
 +
:cssText(self.args.imagestyle)
 +
:wikitext(self.args.image)
 
end
 
end
  
local function renderTrackingCategories(builder)
+
function Navbox:_addLists()
    local title = mw.title.getCurrentTitle()
+
local rowIds, altStyle, altBackground
    if title.namespace ~= 10 then return end -- not in template space
+
local thStyle = {
    local subpage = title.subpageText
+
background = '#ddf',
    if subpage == 'doc' or subpage == 'sandbox' or subpage == 'testcases' then return end
+
['white-space'] = 'nowrap',
 
+
padding = '0 10px',
    for i, cat in ipairs(getTrackingCategories()) do
+
['font-size'] = '90%'
        builder:wikitext('[[Category:' .. cat .. ']]')
+
}
    end
+
-- 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
 
end
  
--
+
-------------------------------------------------------------------------------
--   Main navbox tables
+
--                           classe NavboxSubgroup
--
+
-------------------------------------------------------------------------------
local function renderMainTable()
 
    local tbl = mw.html.create('table')
 
        :addClass('nowraplinks')
 
        :addClass(args.bodyclass)
 
  
    if args.title and (args.state ~= 'plain' and args.state ~= 'off') then
+
local NavboxSubgroup = {}
        tbl
 
            :addClass('collapsible')
 
            :addClass(args.state or 'autocollapse')
 
    end
 
  
    tbl:css('border-spacing', 0)
+
function NavboxSubgroup:new(args)
    if border == 'subgroup' or border == 'child' or border == 'none' then
+
local self = {}
        tbl
 
            :addClass('navbox-subgroup')
 
            :cssText(args.bodystyle)
 
            :cssText(args.style)
 
    else -- regular navobx - bodystyle and style will be applied to the wrapper table
 
        tbl
 
            :addClass('navbox-inner')
 
            :css('background', 'transparent')
 
            :css('color', 'inherit')
 
    end
 
    tbl:cssText(args.innerstyle)
 
  
    renderTitleRow(tbl)
+
setmetatable(self, { __index = NavboxSubgroup })
    renderAboveRow(tbl)
+
self.args = args
    for i, listnum in ipairs(listnums) do
+
-- costruzione table
        renderListRow(tbl, listnum)
+
self.tableNode = mw.html.create('table')
    end
+
if self.args.debug then
    renderBelowRow(tbl)
+
disableCSS(self.tableNode)
 +
end
 +
self:_setupTableNode()
 +
self:_addLists()
  
    return tbl
+
return self
 
end
 
end
  
function p._navbox(navboxArgs)
+
function NavboxSubgroup:getHTML()
    args = navboxArgs
+
return tostring(self.tableNode)
 +
end
  
    for k, v in pairs(args) do
+
function NavboxSubgroup:_setupTableNode()
        local listnum = ('' .. k):match('^list(%d+)$')
+
local tableStyle = {
        if listnum then table.insert(listnums, tonumber(listnum)) end
+
background = 'transparent',
    end
+
['font-size'] = '100%',
    table.sort(listnums)
+
padding = '0',
 +
border = '0',
 +
margin = '-3px',
 +
width = '100%'
 +
}
 +
self.tableNode
 +
:addClass('navbox')
 +
:addClass('nowraplinks')
 +
:css(tableStyle)
 +
:cssText(self.args.bodystyle)
 +
end
  
    border = trim(args.border or args[1] or '')
+
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
  
    -- render the main body of the navbox
+
-------------------------------------------------------------------------------
    local tbl = renderMainTable()
+
--                                    API
 +
-------------------------------------------------------------------------------
  
    -- render the appropriate wrapper around the navbox, depending on the border param
+
local p = {}
    local res = mw.html.create()
 
    if border == 'none' then
 
        local nav = res:tag('div')
 
            :attr('role', 'navigation')
 
            :node(tbl)
 
        if args.title then
 
            nav:attr('aria-labelledby', mw.uri.anchorEncode(args.title))
 
        else
 
            nav:attr('aria-label', 'Navbox')
 
        end
 
    elseif border == 'subgroup' or border == 'child' then
 
        -- We assume that this navbox is being rendered in a list cell of a parent navbox, and is
 
        -- therefore inside a div with padding:0em 0.25em. We start with a </div> to avoid the
 
        -- padding being applied, and at the end add a <div> to balance out the parent's </div>
 
        res
 
            :wikitext('</div>') -- XXX: hack due to lack of unclosed support in mw.html.
 
            :node(tbl)
 
            :wikitext('<div>') -- XXX: hack due to lack of unclosed support in mw.html.
 
    else
 
        local nav = res:tag('div')
 
            :attr('role', 'navigation')
 
            :addClass('navbox')
 
            :cssText(args.bodystyle)
 
            :cssText(args.style)
 
            :css('padding', '3px')
 
            :node(tbl)
 
        if args.title then
 
            nav:attr('aria-labelledby', mw.uri.anchorEncode(args.title))
 
        else
 
            nav:attr('aria-label', 'Navbox')
 
        end
 
    end
 
  
    renderTrackingCategories(res)
+
function p._navbox(args)
 +
return Navbox:new(parseArgs(args)):getHTML()
 +
end
  
    return tostring(res)
+
function p._navbox_subgroup(args)
 +
return NavboxSubgroup:new(parseArgs(args, true)):getHTML()
 
end
 
end
  
 +
-- Entry-point per {{Navbox}}
 
function p.navbox(frame)
 
function p.navbox(frame)
    if not getArgs then
+
return p._navbox(getArgs(frame, {wrappers = 'Template:Navbox'}))
        getArgs = require('Module:Arguments').getArgs
+
end
    end
 
    args = getArgs(frame, {wrappers = 'Template:Navbox'})
 
 
 
    -- Read the arguments in the order they'll be output in, to make references number in the right order.
 
    local _
 
    _ = args.title
 
    _ = args.above
 
    for i = 1, 20 do
 
        _ = args["group" .. tostring(i)]
 
        _ = args["list" .. tostring(i)]
 
    end
 
    _ = args.below
 
  
    return p._navbox(args)
+
-- Entry-point per {{Navbox subgroup}}
 +
function p.navbox_subgroup(frame)
 +
return p._navbox_subgroup(getArgs(frame, {wrappers = 'Template:Navbox subgroup'}))
 
end
 
end
  
 
return p
 
return p

Versione delle 18:44, 1 ago 2015

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