「模組:Navbox」:修訂間差異

出自Reko Wiki
跳至導覽 跳至搜尋
Nekokon留言 | 貢獻
建立內容為「-------------------------------------------------------------------- --<pre> Navbox Module -- -- * Fully CSS styled (inline styles possible but not default) -- * Supports unlimited rows -- -- By User:Tjcool007 from layton.fandom.com -------------------------------------------------------------------- local p = {} local args = {} -- Arguments passed to template local navbox -- Actual navbox --local working = {} local rownums, skiprows = {}, {} local has…」的新頁面
 
Nekokon留言 | 貢獻
Wikipedia Version
標籤已被回退
第1行: 第1行:
--------------------------------------------------------------------
--<pre> Navbox Module
--
--
-- * Fully CSS styled (inline styles possible but not default)
-- This module will implement {{Navbox}}
-- * Supports unlimited rows
--
--
-- By User:Tjcool007 from layton.fandom.com
--------------------------------------------------------------------
   
   
local p = {}
local p = {}
   
   
local args = {} -- Arguments passed to template
local navbar = require('Module:Navbar')._navbar
local navbox -- Actual navbox
local getArgs -- lazily initialized
 
--local working = {}
local args
local rownums, skiprows = {}, {}
local tableRowAdded = false
local hasrows, alt, hasData, isChild = false, false, false, false
local border
local activeSection, sections, cimage, cimageleft
local listnums = {}
local colspan, rowspan
 
local function trim(s)
local showText, hideText = '展開', '摺疊'
    return (mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1"))
end
local langCode = mw.getContentLanguage():getCode()
 
local localization = {} --localized strings table
local function addNewline(s)
localization['en'] = {show = 'Show', hide = 'Hide'}
    if s:match('^[*:;#]') or s:match('^{|') then
localization['ru'] = {show = 'показать', hide = 'скрыть'}
        return '\n' .. s ..'\n'
localization['zh'] = {show = '展開', hide = '摺疊'}
    else
if localization[langCode] then
        return s
    showText = localization[langCode]['show']
     end
     hideText = localization[langCode]['hide']
end
end
 
------------------------------------------------
local function addTableRow(tbl)
-- Title
    -- If any other rows have already been added, then we add a 2px gutter row.
------------------------------------------------
    if tableRowAdded then
        tbl
--- Processes the VDE links in the title
            :tag('tr')
--
                :css('height', '2px')
-- @param titlecell The table cell of the title
                :tag('td')
local function processVde( titlecell )
                :attr('colspan', 3)
if not args.template then return end
    end
   
titlecell:wikitext('<span class="navbox-vde">'
    tableRowAdded = true
.. mw.getCurrentFrame():expandTemplate({
   
title = 'vdelinks',
    return tbl:tag('tr')
args = { args.template, ['type'] = 'navbox' }
}) .. '</span>')
end
end
 
--- Processes the main title row
local function renderNavBar(titleCell)
local function processTitle()
    -- Depending on the presence of the navbar and/or show/hide link, we may need to add a spacer div on the left
local titlerow = mw.html.create('tr'):addClass('navbox-title')
    -- or right to keep the title centered.
local titlecell = mw.html.create('th'):attr('colspan',colspan):attr('scope','col')
    local spacerSide = nil
 
if not pcall( processVde, titlecell ) then
    if args.navbar == 'off' then
titlecell:wikitext( '<b class="navbox-vde error" title="Missing Template:Vdelinks">!!!</b>' )
        -- No navbar, and client wants no spacer, i.e. wants the title to be shifted to the left. If there's
end
        -- 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
titlecell:wikitext( args.title or '{{{title}}}' )
    elseif args.navbar == 'plain' or (not args.name and mw.getCurrentFrame():getParent():getTitle() == 'Template:Navbox' and (border == 'subgroup' or border == 'child' or border == 'none')) then
        -- No navbar. Need a spacer on the left to balance out the width of the show/hide link.
-- Padding
        if args.state ~= 'plain' then spacerSide = 'left' end
local hasTemplate = args.template ~= nil
    else
local hasState = not args.state or args.state ~= 'plain'
        -- 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 hasTemplate ~= hasState then
        if args.state == 'plain' then spacerSide = 'right' end
if hasTemplate then
 
titlecell:addClass('navbox-title-padright')
        titleCell:wikitext(navbar{  
else
            args.name,
titlecell:addClass('navbox-title-padleft')
            mini = 1,
end
            fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') ..  ';background:none transparent;border:none;'
end
        })
    end
if args.titleclass then titlerow:addClass( args.titleclass ) end
   
if args.titlestyle then titlecell:cssText( args.titlestyle ) end
    -- Render the spacer div.
    if spacerSide then
titlerow:node(titlecell)
        titleCell
navbox:node(titlerow)
            :tag('span')
                :css('float', spacerSide)
                :css('width', '8em')
                :css('font-size', '80%')
                :css('margin-' .. (spacerSide == 'left' and 'right' or 'left'), '0.5em')
                :wikitext('&nbsp;')
    end
end
end
--
--  Title row
--
local function renderTitleRow(tbl)
    if not args.title then return end
   
   
local function _addGutter( parent, incRowspan )
    local titleRow = addTableRow(tbl)
parent:tag('tr'):addClass('navbox-gutter'):tag('td'):attr('colspan',2)
   
    if args.titlegroup then
        titleRow
            :tag('th')
                :attr('scope', 'row')
                :addClass('navbox-group')
                :addClass(args.titlegroupclass)
                :cssText(args.basestyle)
                :cssText(args.groupstyle)
                :cssText(args.titlegroupstyle)
                :wikitext(args.titlegroup)
    end
   
    local titleCell = titleRow:tag('th'):attr('scope', 'col')
           
    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')
        :addClass(args.titleclass)
        :attr('colspan', titleColspan)
   
   
if incRowspan then
    renderNavBar(titleCell)
rowspan = rowspan + 1
 
end
    titleCell
        :tag('div')
            :css('font-size', '110%')
            :wikitext(addNewline(args.title))
end
end
 
------------------------------------------------
--
-- Above/Below
--   Above/Below rows
------------------------------------------------
--- Processes the above and below rows
--
--
-- @param rowtype Either 'above' or 'below'
 
local function processAboveBelow( rowtype )
local function getAboveBelowColspan()
if not args[rowtype] then return end
    local ret = 2
    if args.imageleft then ret = ret + 1 end
local abrow = mw.html.create('tr'):addClass('navbox-'..rowtype)
    if args.image then ret = ret + 1 end
local abcell = mw.html.create('td'):attr('colspan',colspan):wikitext( args[rowtype] )
    return ret
if args[rowtype .. 'class'] then abrow:addClass( args[rowtype .. 'class'] ) end
if args[rowtype .. 'style'] then abcell:cssText( args[rowtype .. 'style'] ) end
abrow:node( abcell )
_addGutter( navbox )
navbox:node( abrow )
end
end
 
------------------------------------------------
local function renderAboveRow(tbl)
-- Main Rows
    if not args.above then return end
------------------------------------------------
 
    addTableRow(tbl)
--- Processes the images
        :tag('td')
local function _processImage(row, imgtype)
            :addClass('navbox-abovebelow')
if not args[imgtype] then return end
            :addClass(args.aboveclass)
            :cssText(args.basestyle)
local iclass = imgtype == 'image' and 'navbox-image-right' or 'navbox-image-left'
            :cssText(args.abovestyle)
            :attr('colspan', getAboveBelowColspan())
local imagecell = mw.html.create('td'):addClass('navbox-image'):addClass(iclass)
            :tag('div')
                :wikitext(addNewline(args.above))
local image = args[imgtype]
if image:sub(1,1) ~= '[' then
local width = args[imgtype .. 'width'] or '100px'
imagecell:css('width',width):wikitext('['..'[' .. image  .. '|' .. width .. '|link=' .. (args[imgtype .. 'link'] or '') .. ']]')
else
imagecell:css('width','100%'):wikitext(image)
end
if args[imgtype .. 'class'] then imagecell:addClass( args[imgtype .. 'class'] ) end
if args[imgtype .. 'style'] then imagecell:cssText( args[imgtype .. 'style'] ) end
row:node( imagecell )
if imgtype == 'image' then
cimage = imagecell
else
cimageleft = imagecell
end
end
end
 
--- Closes the currently active section (if any)
local function renderBelowRow(tbl)
local function _closeCurrentSection()
    if not args.below then return end
if not activeSection then return end
 
    addTableRow(tbl)
local row = mw.html.create('tr'):addClass('navbox-section-row')
        :tag('td')
local cell = mw.html.create('td'):attr('colspan',2)
            :addClass('navbox-abovebelow')
            :addClass(args.belowclass)
if not hasrows then
            :cssText(args.basestyle)
_processImage(row,'imageleft')
            :cssText(args.belowstyle)
end
            :attr('colspan', getAboveBelowColspan())
            :tag('div')
cell:node(sections[activeSection])
                :wikitext(addNewline(args.below))
row:node(cell)
local firstRow = false
if not hasrows then
firstRow = true
hasrows = true
_processImage(row,'image')
end
_addGutter(navbox,not firstRow)
navbox:node(row)
rowspan = rowspan + 1
activeSection = false
hasData = false
end
end
   
   
--- Handles alternating rows
--
--
-- @return Alternatingly returns true or false. Always returns false if alternating rows
--   List rows
--        are disabled with "alternaterows = no"
local function _alternateRow()
if args.alternaterows == 'no' then return false end
if alt then
alt = false
return true
else
alt = true
return false
end
end
--- Process a single Header "row"
--
--
-- @param num Number of the row to be processed
local function renderListRow(tbl, listnum)
local function processHeader(num)
    local row = addTableRow(tbl)
if not args['header'..num] then return end
   
    if listnum == 1 and args.imageleft then
_closeCurrentSection()
        row
            :tag('td')
local subtable = mw.html.create('table'):addClass('navbox-section')
                :addClass('navbox-image')
local headerrow = mw.html.create('tr')
                :addClass(args.imageclass)
local header = mw.html.create('th'):addClass('navbox-header'):attr('colspan',2):attr('scope','col'):wikitext( args['header'..num] )
                :css('width', '0%')
                :css('padding', '0px 2px 0px 0px')
local collapseme = args['state'..num] or false
                :cssText(args.imageleftstyle)
local state = false
                :attr('rowspan', 2 * #listnums - 1)
                :tag('div')
if collapseme then
                    :wikitext(addNewline(args.imageleft))
-- Look at this one
    end
if collapseme ~= 'plain' then
state = collapseme == 'expanded' and 'expanded' or 'collapsed'
end
else
-- Look at default
local collapseall = args.defaultstate or false
if collapseall then
state = collapseall == 'expanded' and 'expanded' or 'collapsed'
end
end
   
   
if state then
    if args['group' .. listnum] then
subtable:addClass('mw-collapsible'):attr('data-expandtext',args['expandtext'..num] or args['defaultexpandtext'] or showText):attr('data-collapsetext',args['collapsetext'..num] or args['defaultcollapsetext'] or hideText)
        local groupCell = row:tag('th')
if state == 'collapsed' then
       
subtable:addClass('mw-collapsed')
        groupCell
end
              :attr('scope', 'row')
header:addClass('navbox-header-collapsible')
              :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')
 
    if args['group' .. listnum] then
        listCell
            :css('text-align', 'left')
            :css('border-left-width', '2px')
            :css('border-left-style', 'solid')
    else
        listCell:attr('colspan', 2)
    end
   
    if not args.groupwidth then
        listCell:css('width', '100%')
    end
   
    local isOdd = (listnum % 2) == 1
    local rowstyle = args.evenstyle
    if isOdd then rowstyle = args.oddstyle end
   
   
if args.headerclass then headerrow:addClass( args.headerclass ) end
    local evenOdd
if args.headerstyle then header:cssText( args.headerstyle ) end
    if args.evenodd == 'swap' then
        if isOdd then evenOdd = 'even' else evenOdd = 'odd' end
headerrow:node(header)
    else
subtable:node(headerrow)
        if isOdd then evenOdd = args.evenodd or 'odd' else evenOdd = args.evenodd or 'even' end
    end
sections[num] = subtable
 
activeSection = num
    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
 
--- Processes a single list row
 
--
--   Tracking categories
--
--
-- @param num Number of the row to be processed
 
local function processList(num)
local function needsHorizontalLists()
if not args['list'..num] then return end
    if border == 'child' or border == 'subgroup'  or args.tracking == 'no' then return false end
   
local row = mw.html.create('tr'):addClass('navbox-row')
    local listClasses = {'plainlist', 'hlist', 'hlist hnum', 'hlist hwrap', 'hlist vcard', 'vcard hlist', 'hlist vevent', 'hlist hlist-pipe'}
    for i, cls in ipairs(listClasses) do
if not hasrows and not activeSection then
        if args.listclass == cls or args.bodyclass == cls then
_processImage(row, 'imageleft')
            return false
end
        end
    end
local listcell = mw.html.create('td'):addClass('navbox-list')
 
local hlistcell = listcell:tag('div'):addClass('hlist')
    return true
end
local data = args['list'..num]
 
local function hasBackgroundColors()
if data:sub(1,1) == '*' then
    return mw.ustring.match(args.titlestyle or '','background') or mw.ustring.match(args.groupstyle or '','background') or mw.ustring.match(args.basestyle or '','background')
-- Add newlines to support lists properly
end
hlistcell
 
:newline()
local function argNameAndRealTitleAreDifferent()
:wikitext( data )
if border == 'child' or border == 'subgroupor args.tracking == 'no' then return false end
:newline()
 
else
if args.name ~= mw.title.getCurrentTitle().text then
hlistcell:wikitext( data )
return true
end
local altRow = _alternateRow()
if altRow then
row:addClass( args.altrowclass or 'alt' )
local listclass = args.altlistclass or args.listclass or false
if listclass then listcell:addClass( listclass ) end
local liststyle = args.altliststyle or args.liststyle or false
if liststyle then listcell:cssText( liststyle ) end
else
if args.rowclass then row:addClass( args.rowclass ) end
if args.listclass then listcell:addClass( args.listclass ) end
if args.liststyle then listcell:cssText( args.liststyle ) end
end
if args['group'..num] then
local groupcell = mw.html.create('th'):addClass('navbox-group'):attr('scope','row'):wikitext( args['group'..num] )
if altRow then
local groupclass = args.altgroupclass or args.groupclass or false
if groupclass then groupcell:addClass( groupclass ) end
local groupstyle = args.altgroupstyle or args.groupstyle or false
if groupstyle then groupcell:cssText( groupstyle ) end
else
if args.groupclass then groupcell:addClass( args.groupclass ) end
if args.groupstyle then groupcell:cssText( args.groupstyle ) end
end
row:node( groupcell )
else
listcell:attr('colspan',2):addClass('no-group')
end
row:node( listcell )
local firstRow = false
if not hasrows and not activeSection then
firstRow = true
hasrows = true
_processImage(row, 'image')
end
   
if activeSection then
local parent = sections[activeSection]
if not isChild or not firstRow then
_addGutter(parent)
end
parent:node(row)
hasData = true
else
if not isChild or not firstRow then
_addGutter(navbox,not firstRow)
end
navbox:node( row )
rowspan = rowspan + 1
end
end
return false
end
local function getTrackingCategories()
    local cats = {}
    if needsHorizontalLists() then table.insert(cats, '没有使用水平列表的导航框') end
    if hasBackgroundColors() then table.insert(cats, '使用背景颜色的导航框') end
    if argNameAndRealTitleAreDifferent() then table.insert(cats, 'name參數和實際不同的導航框') end
    return cats
end
end
 
--- Processes all rows
local function renderTrackingCategories(builder)
local function processRows()
    local title = mw.title.getCurrentTitle()
sections = {}
    if title.namespace ~= 10 then return end -- not in template space
for i=1,#rownums do
    local subpage = title.subpageText
local num = rownums[i]
    if subpage == 'doc' or subpage == 'sandbox' or subpage == 'testcases' then return end
if not skiprows[num] then
   
processHeader(num)
    for i, cat in ipairs(getTrackingCategories()) do
processList(num)
        builder:wikitext('[[Category:' .. cat .. ']]')  
end
    end
end
_closeCurrentSection()
if cimageleft then
cimageleft:attr('rowspan',rowspan)
end
if cimage then
cimage:attr('rowspan',rowspan)
end
end
end
 
------------------------------------------------
--
-- ARGUMENTS PREPROCESSOR
--   Main navbox tables
-- * Extracts arguments from frame and stores them in args table
-- * At the same time, checks for valid row numbers
------------------------------------------------
--- Preprocessor for the arguments.
-- Will fill up the args table with the parameters from the frame grouped by their type.
--
--
-- @param frame The frame passed to the Module.
local function renderMainTable()
local function preProcessArgs(frame)
    local tbl = mw.html.create('table')
local tmp = {}
        :attr('cellspacing', 0)
        :addClass('nowraplinks')
if frame == mw.getCurrentFrame() then
        :addClass(args.bodyclass)
tmp = frame:getParent().args
           
else
    if args.title and (args.state ~= 'plain' and args.state ~= 'off') then
tmp = frame
        tbl
end
            :addClass('collapsible')
            :addClass(args.state or 'autocollapse')
-- Storage tables
    end
local nums = {}
-- Loop over all the args
for k,v in pairs(tmp) do
-- Skip empty args, which are useless
if v ~= '' then
local cat,num = tostring(k):match('^(%a+)([1-9]%d*)$')
if cat == 'header' or cat == 'list' then
nums[num] = true
end
args[k] = v -- Simple copy
end
end
colspan = args.image and 3 or 2
if args.imageleft then colspan = colspan + 1 end
rowspan = 0
if args.alternaterows == 'swap' then
alt = true
end
for k, v in pairs(nums) do
rownums[#rownums+1] = tonumber(k)
end
   
   
table.sort(rownums)
    tbl:css('border-spacing', 0)
    if border == 'subgroup' or border == 'child' or border == 'none' then
        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)
   
   
-- Calculate skip rows
    renderTitleRow(tbl)
local cSection, cSkip
    renderAboveRow(tbl)
local showall = args.showall
    for i, listnum in ipairs(listnums) do
for i=1,#rownums do
        renderListRow(tbl, listnum)  
local num = rownums[i]
    end
if args['header'..num] then
    renderBelowRow(tbl)
cSection = true
   
cSkip = false
    return tbl
local showme = args['show'..num]
if showme == 'no' then
cSkip = true
elseif showme == 'auto' or (showme ~= 'yes' and showall ~= 'yes') then
if not args['list'..num] then
local nextNum = rownums[i+1]
cSkip = not nextNum or args['header'..nextNum] -- If next has a header -> skip
end
end
end
if cSection and cSkip then
skiprows[num] = true
end
end
end
end
function p._navbox(navboxArgs)
    args = navboxArgs
   
    for k, v in pairs(args) do
        local listnum = ('' .. k):match('^list(%d+)$')
        if listnum then table.insert(listnums, tonumber(listnum)) end
    end
    table.sort(listnums)
   
   
------------------------------------------------
    border = trim(args.border or args[1] or '')
-- MAIN FUNCTIONS
 
------------------------------------------------
    -- render the main body of the navbox
    local tbl = renderMainTable()
--- Processes the arguments to create the navbox.
 
--
    -- render the appropriate wrapper around the navbox, depending on the border param
-- @return A string with HTML that is the navbox.
    local res = mw.html.create()
local function _navbox()
    if border == 'none' then
-- Create the root HTML element
        res:node(tbl)
local trim = function(s)
    elseif border == 'subgroup' or border == 'child' then
return s and mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1") or ''
        -- We assume that this navbox is being rendered in a list cell of a parent navbox, and is
end
        -- therefore inside a div with padding:0em 0.25em. We start with a </div> to avoid the
local border = args.border or trim(args[1])  or ''
        -- padding being applied, and at the end add a <div> to balance out the parent's </div>
isChild = (border == 'child' or border == 'subgroup')
        res
            :wikitext('</div>') -- mw.html 未支持 unclosed
if isChild then
            :node(tbl)
navbox = mw.html.create('table'):addClass('navbox-subgroup')
            :wikitext('<div>') -- mw.html 未支持 unclosed
else
    else
navbox = mw.html.create('table'):addClass('navbox')
        res
            :tag('table')
if args.state ~= 'plain' then
                :attr('cellspacing', 0)
navbox:addClass('mw-collapsible'):attr('data-expandtext',args['expandtext'] or args['defaultexpandtext'] or showText):attr('data-collapsetext',args['collapsetext'] or args['defaultcollapsetext'] or hideText)
                :addClass('navbox')
if args.state == 'collapsed' then
                :css('border-spacing', 0)
navbox:addClass('mw-collapsed')
                :cssText(args.bodystyle)
end
                :cssText(args.style)
end
                :tag('tr')
end
                    :tag('td')
                        :css('padding', '2px')
if args.bodyclass then navbox:addClass(args.bodyclass) end
                        :node(tbl)
if args.bodystyle then navbox:cssText(args.bodystyle) end
    end
   
   
-- Process...
    renderTrackingCategories(res)
if not isChild then
processTitle()
processAboveBelow('above')
processRows()
processAboveBelow('below')
   
   
return tostring(navbox)
    return tostring(res)
else
processRows()
local wrapper = mw.html.create('')
wrapper:wikitext('</div>')
wrapper:node(navbox)
wrapper:wikitext('<div class="hlist">')
return tostring(wrapper)
end
end
end
   
   
--- Main module entry point.
function p.navbox(frame)
-- To be called with {{#invoke:navbox|main}} or directly from another module.
    if not getArgs then
--
    getArgs = require('Module:Arguments').getArgs
-- @param frame The frame passed to the module via the #invoke. If called from another
    end
--              module directly, this should be a table with the parameter definition.
    args = getArgs(frame, {wrappers = 'Template:Navbox'})
function p.main(frame)
 
-- Save the arguments in a local variable so other functions can use them.
    -- Read the arguments in the order they'll be output in, to make references number in the right order.
preProcessArgs(frame)
    local _
    _ = args.title
return _navbox()
    _ = args.above
    for i = 1, 35 do
        _ = args["group" .. tostring(i)]
        _ = args["list" .. tostring(i)]
    end   
    _ = args.below
 
    return p._navbox(args)
end
end
   
   
return p
return p

於 2022年12月2日 (五) 22:12 的修訂

可在模組:Navbox/doc建立此模組的說明文件

--
-- This module will implement {{Navbox}}
--
 
local p = {}
 
local navbar = require('Module:Navbar')._navbar
local getArgs -- lazily initialized

local args
local tableRowAdded = false
local border
local listnums = {}

local function trim(s)
    return (mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1"))
end

local function addNewline(s)
    if s:match('^[*:;#]') or s:match('^{|') then
        return '\n' .. s ..'\n'
    else
        return s
    end
end

local function addTableRow(tbl)
    -- If any other rows have already been added, then we add a 2px gutter row.
    if tableRowAdded then
        tbl
            :tag('tr')
                :css('height', '2px')
                :tag('td')
                	:attr('colspan', 3)
    end
    
    tableRowAdded = true
    
    return tbl:tag('tr')
end

local function renderNavBar(titleCell)
    -- Depending on the presence of the navbar and/or show/hide link, we may need to add a spacer div on the left
    -- or right to keep the title centered.
    local spacerSide = nil

    if args.navbar == 'off' then
        -- 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() == 'Template:Navbox' and (border == 'subgroup' or border == 'child' or border == 'none')) 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{ 
            args.name, 
            mini = 1, 
            fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') ..  ';background:none transparent;border:none;'
        })
    end
    
    -- Render the spacer div.
    if spacerSide then
        titleCell
            :tag('span')
                :css('float', spacerSide)
                :css('width', '8em')
                :css('font-size', '80%')
                :css('margin-' .. (spacerSide == 'left' and 'right' or 'left'), '0.5em')
                :wikitext('&nbsp;')
    end
end

--
--   Title row
--
local function renderTitleRow(tbl)
    if not args.title then return end
 
    local titleRow = addTableRow(tbl)
     
    if args.titlegroup then
        titleRow
            :tag('th')
                :attr('scope', 'row')
                :addClass('navbox-group')
                :addClass(args.titlegroupclass)
                :cssText(args.basestyle)
                :cssText(args.groupstyle)
                :cssText(args.titlegroupstyle)
                :wikitext(args.titlegroup)
    end
    
    local titleCell = titleRow:tag('th'):attr('scope', 'col')
            
    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')
        :addClass(args.titleclass)
        :attr('colspan', titleColspan)
 
    renderNavBar(titleCell)

    titleCell
         :tag('div')
             :css('font-size', '110%')
             :wikitext(addNewline(args.title))
end

--
--   Above/Below rows
--

local function getAboveBelowColspan()
    local ret = 2
    if args.imageleft then ret = ret + 1 end
    if args.image then ret = ret + 1 end
    return ret
end

local function renderAboveRow(tbl)
    if not args.above then return end

    addTableRow(tbl)
        :tag('td')
            :addClass('navbox-abovebelow')
            :addClass(args.aboveclass)
            :cssText(args.basestyle)
            :cssText(args.abovestyle)
            :attr('colspan', getAboveBelowColspan())
            :tag('div')
                :wikitext(addNewline(args.above))
end

local function renderBelowRow(tbl)
    if not args.below then return end

    addTableRow(tbl)
        :tag('td')
            :addClass('navbox-abovebelow')
            :addClass(args.belowclass)
            :cssText(args.basestyle)
            :cssText(args.belowstyle)
            :attr('colspan', getAboveBelowColspan())
            :tag('div')
                :wikitext(addNewline(args.below))
end
 
--
--   List rows
--
local function renderListRow(tbl, listnum)
    local row = addTableRow(tbl)
    
    if listnum == 1 and args.imageleft then
        row
            :tag('td')
                :addClass('navbox-image')
                :addClass(args.imageclass)
                :css('width', '0%')
                :css('padding', '0px 2px 0px 0px')
                :cssText(args.imageleftstyle)
                :attr('rowspan', 2 * #listnums - 1)
                :tag('div')
                    :wikitext(addNewline(args.imageleft))
    end
 
    if args['group' .. listnum] then
        local groupCell = row:tag('th')
        
        groupCell
               :attr('scope', 'row')
               :addClass('navbox-group')
               :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')

    if args['group' .. listnum] then
        listCell
            :css('text-align', 'left')
            :css('border-left-width', '2px')
            :css('border-left-style', 'solid')
    else
        listCell:attr('colspan', 2)
    end
    
    if not args.groupwidth then 
        listCell:css('width', '100%')
    end
    
    local isOdd = (listnum % 2) == 1
    local rowstyle = args.evenstyle
    if isOdd then rowstyle = args.oddstyle end
 
    local evenOdd
    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


--
--   Tracking categories
--

local function needsHorizontalLists()
    if border == 'child' or border == 'subgroup'  or args.tracking == 'no' then return false end
    
    local listClasses = {'plainlist', 'hlist', 'hlist hnum', 'hlist hwrap', 'hlist vcard', 'vcard hlist', 'hlist vevent', 'hlist hlist-pipe'}
    for i, cls in ipairs(listClasses) do
        if args.listclass == cls or args.bodyclass == cls then
            return false
        end
    end

    return true
end

local function hasBackgroundColors()
    return mw.ustring.match(args.titlestyle or '','background') or mw.ustring.match(args.groupstyle or '','background') or mw.ustring.match(args.basestyle or '','background')
end

local function argNameAndRealTitleAreDifferent()
	if border == 'child' or border == 'subgroup'  or args.tracking == 'no' then return false end

	if args.name ~= mw.title.getCurrentTitle().text then
		return true
	end
	return false
end

local function getTrackingCategories()
    local cats = {}
    if needsHorizontalLists() then table.insert(cats, '没有使用水平列表的导航框') end
    if hasBackgroundColors() then table.insert(cats, '使用背景颜色的导航框') end
    if argNameAndRealTitleAreDifferent() then table.insert(cats, 'name參數和實際不同的導航框') end
    return cats
end

local function renderTrackingCategories(builder)
    local title = mw.title.getCurrentTitle()
    if title.namespace ~= 10 then return end -- not in template space
    local subpage = title.subpageText
    if subpage == 'doc' or subpage == 'sandbox' or subpage == 'testcases' then return end
    
    for i, cat in ipairs(getTrackingCategories()) do
        builder:wikitext('[[Category:' .. cat .. ']]') 
    end
end

--
--   Main navbox tables
--
local function renderMainTable()
    local tbl = mw.html.create('table')
        :attr('cellspacing', 0)
        :addClass('nowraplinks')
        :addClass(args.bodyclass)
             
    if args.title and (args.state ~= 'plain' and args.state ~= 'off') then
        tbl
            :addClass('collapsible')
            :addClass(args.state or 'autocollapse')
    end
 
    tbl:css('border-spacing', 0)
    if border == 'subgroup' or border == 'child' or border == 'none' then
        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)
    renderAboveRow(tbl)
    for i, listnum in ipairs(listnums) do
        renderListRow(tbl, listnum) 
    end
    renderBelowRow(tbl)
    
    return tbl
end

function p._navbox(navboxArgs)
    args = navboxArgs
    
    for k, v in pairs(args) do
        local listnum = ('' .. k):match('^list(%d+)$')
        if listnum then table.insert(listnums, tonumber(listnum)) end
    end
    table.sort(listnums)
 
    border = trim(args.border or args[1] or '')

    -- render the main body of the navbox
    local tbl = renderMainTable()

    -- render the appropriate wrapper around the navbox, depending on the border param
    local res = mw.html.create()
    if border == 'none' then
        res:node(tbl)
    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>') -- mw.html 未支持 unclosed
            :node(tbl)
            :wikitext('<div>') -- mw.html 未支持 unclosed
    else
        res
            :tag('table')
                :attr('cellspacing', 0)
                :addClass('navbox')
                :css('border-spacing', 0)
                :cssText(args.bodystyle)
                :cssText(args.style)
                :tag('tr')
                    :tag('td')
                        :css('padding', '2px')
                        :node(tbl)
    end
 
    renderTrackingCategories(res)
 
    return tostring(res)
end
 
function p.navbox(frame)
    if not getArgs then
    	getArgs = require('Module:Arguments').getArgs
    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, 35 do
        _ = args["group" .. tostring(i)]
        _ = args["list" .. tostring(i)]
    end    
    _ = args.below

    return p._navbox(args)
end
 
return p