|
|
| (2 intermediate revisions by one other user not shown) |
| Line 1: |
Line 1: |
| local p = {} | | local p = {} |
| local getArgs -- lazily initialized
| | function p.box( f ) |
| local args | | local args = require( 'Module:ProcessArgs' ).merge( true ) |
| local format = string.format | | local navbox = {} |
|
| |
|
| local function get_title_arg(is_collapsible, template)
| | if args.title then |
| local title_arg = 1
| | local class = args.class or 'collapsible' |
| if is_collapsible then title_arg = 2 end | | local bodyStyle = args.bodystyle or '' |
| if template then title_arg = 'template' end
| | if bodyStyle ~= '' then |
| return title_arg
| | bodyStyle = 'style="' .. bodyStyle .. '"' |
| end
| |
| | |
| local function add_link(link_description, ul, is_mini)
| |
| local l
| |
| if link_description.url then
| |
| l = {'[', '', ']'}
| |
| else
| |
| l = {'[[', '|', ']]'}
| |
| end
| |
| ul:tag('li')
| |
| :addClass('nv-' .. link_description.full)
| |
| :wikitext(l[1] .. link_description.link .. l[2])
| |
| :tag(is_mini and 'abbr' or 'span')
| |
| :attr('title', link_description.html_title)
| |
| :wikitext(is_mini and link_description.mini or link_description.full)
| |
| :done()
| |
| :wikitext(l[3])
| |
| :done()
| |
| end
| |
| | |
| local function make_list(title_text, has_brackets, is_mini)
| |
| local title = mw.title.new(mw.text.trim(title_text), 'Template')
| |
| if not title then
| |
| error('Invalid title ' .. title_text) | |
| end
| |
| local talkpage = title.talkPageTitle and title.talkPageTitle.fullText or ''
| |
|
| |
| local link_descriptions = {
| |
| { ['mini'] = 'v', ['full'] = 'view', ['html_title'] = 'View this template',
| |
| ['link'] = title.fullText, ['url'] = false },
| |
| { ['mini'] = 'e', ['full'] = 'edit', ['html_title'] = 'Edit this template',
| |
| ['link'] = title:fullUrl('action=edit'), ['url'] = true },
| |
| { ['mini'] = 'h', ['full'] = 'hist', ['html_title'] = 'History of this template', | |
| ['link'] = title:fullUrl('action=history'), ['url'] = true },
| |
| }
| |
| | |
| local ul = mw.html.create('ul')
| |
| if has_brackets then
| |
| ul:addClass('navbar-brackets')
| |
| end
| |
|
| |
| for _, description in ipairs(link_descriptions) do
| |
| add_link(description, ul, is_mini) | |
| end
| |
| return ul:done()
| |
|
| |
| end
| |
| | |
| local function navbar(args)
| |
| local is_collapsible = args.collapsible
| |
| local is_mini = args.mini
| |
| local is_plain = args.plain
| |
|
| |
| local collapsible_class = nil
| |
| if is_collapsible then
| |
| collapsible_class = 'navbar-collapse'
| |
| if not is_plain then is_mini = 1 end
| |
| end
| |
|
| |
| local div = mw.html.create():tag('div')
| |
| div
| |
| :addClass('navbar')
| |
| :addClass('plainlinks')
| |
| :addClass('hlist')
| |
| :addClass(collapsible_class) -- we made the determination earlier
| |
| | |
| if is_mini then div:addClass('navbar-mini') end
| |
| | |
| local box_text = (args.text or 'This box: ') .. ' '
| |
| -- the concatenated space guarantees the box text is separated
| |
| if not (is_mini or is_plain) then
| |
| div
| |
| :tag('span')
| |
| :addClass('navbar-boxtext')
| |
| :wikitext(box_text)
| |
| end
| |
|
| |
| local template = args.template
| |
| local has_brackets = args.brackets
| |
| local title_arg = get_title_arg(is_collapsible, template)
| |
| local title_text = args[title_arg] or (':' .. mw.getCurrentFrame():getParent():getTitle())
| |
| local list = make_list(title_text, has_brackets, is_mini)
| |
| div:node(list)
| |
| | |
| if is_collapsible then
| |
| local title_text_class
| |
| if is_mini then
| |
| title_text_class = 'navbar-ct-mini'
| |
| else
| |
| title_text_class = 'navbar-ct-full'
| |
| end | | end |
| div:done() | | table.insert( navbox, ' {| class="navbox hlist ' .. class .. '" ' .. bodyStyle ) |
| :tag('div')
| | |
| :addClass(title_text_class)
| | local titleStyle = args.titlestyle or '' |
| :wikitext(args[1])
| | if titleStyle ~= '' then |
| end
| | titleStyle = 'style="' .. titleStyle .. '"' |
|
| |
| return tostring(div:done())
| |
| end
| |
| | |
| local function striped(wikitext, border)
| |
| -- Return wikitext with markers replaced for odd/even striping.
| |
| -- Child (subgroup) navboxes are flagged with a category that is removed
| |
| -- by parent navboxes. The result is that the category shows all pages
| |
| -- where a child navbox is not contained in a parent navbox.
| |
| if border == 'subgroup' and args['orphan'] ~= 'yes' then
| |
| -- No change; striping occurs in outermost navbox. | |
| return wikitext | |
| end
| |
| local first, second = 'odd', 'even'
| |
| if args['evenodd'] then
| |
| if args['evenodd'] == 'swap' then | |
| first, second = second, first | |
| else
| |
| first = args['evenodd']
| |
| second = first
| |
| end | | end |
| end
| | local navbar = args[1] or '' |
| local changer
| | if navbar ~= '' then |
| if first == second then
| | local mini = '' |
| changer = first
| | if navbar:match( 'navbar%-mini' ) then |
| else
| | mini = '1' |
| local index = 0
| |
| changer = function (code)
| |
| if code == '0' then | |
| -- Current occurrence is for a group before a nested table. | |
| -- Set it to first as a valid although pointless class.
| |
| -- The next occurrence will be the first row after a title
| |
| -- in a subgroup and will also be first.
| |
| index = 0
| |
| return first
| |
| end | | end |
| index = index + 1 | | navbar = '<div class="navbox-navbar">' .. f:expandTemplate( { |
| return index % 2 == 1 and first or second
| | title = 'navbar', |
| | args = { |
| | args.name, |
| | mini = mini |
| | } |
| | } ) .. '</div>' |
| end | | end |
| | table.insert( navbox, '! class="navbox-top" colspan="2" ' .. titleStyle .. ' | ' .. navbar .. '<span class="navbox-title">' .. args.title .. '</span>' ) |
| | else |
| | table.insert( navbox, ' {| class="navbox-child"' ) |
| end | | end |
| return (wikitext:gsub('\127_ODDEVEN(%d?)_\127', changer)) -- () omits gsub count | | |
| end
| | local groupNums = {} |
| | | for k, v in pairs( args ) do |
| local function processItem(item, nowrapitems) | | if type( k ) == 'string' then |
| if item:sub(1, 2) == '{|' then
| | local groupNum = k:match( 'group(%d+)' ) |
| -- Applying nowrap to lines in a table does not make sense.
| | if groupNum and v then |
| -- Add newlines to compensate for trim of x in |parm=x in a template.
| | table.insert( groupNums, tonumber( groupNum ) ) |
| return '\n' .. item ..'\n' | |
| end
| |
| if nowrapitems == 'yes' then
| |
| local lines = {}
| |
| for line in (item .. '\n'):gmatch('([^\n]*)\n') do
| |
| local prefix, content = line:match('^([*:;#]+)%s*(.*)') | |
| if prefix and not content:match('^<span class="nowrap">') then | |
| line = format('%s<span class="nowrap">%s</span>', prefix, content) | |
| end | | end |
| table.insert(lines, line)
| |
| end | | end |
| item = table.concat(lines, '\n')
| |
| end | | end |
| if item:match('^[*:;#]') then | | table.sort( groupNums ) |
| return '\n' .. item ..'\n'
| | |
| | local groupStyle = args.groupstyle or '' |
| | local listStyle = args.liststyle or '' |
| | for _, v in ipairs( groupNums ) do |
| | local list = args['list' .. v] |
| | if list then |
| | table.insert( navbox, '|-\n! class="navbox-group" style="' .. groupStyle .. '" | ' .. args['group' .. v] ) |
| | table.insert( navbox, '| class="navbox-list" style="' .. listStyle .. '" | ' .. list:gsub( '^([*#:{])', '\n%1' ) ) |
| | end |
| end | | end |
| return item
| |
| end
| |
|
| |
| -- we will want this later when we want to add tstyles for hlist/plainlist
| |
| local function has_navbar()
| |
| return args['navbar'] ~= 'off'
| |
| and args['navbar'] ~= 'plain'
| |
| and (
| |
| args['name']
| |
| or mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', '')
| |
| ~= 'Template:Navbox'
| |
| )
| |
| end
| |
|
| |
| local function renderNavBar(titleCell)
| |
| if has_navbar() then
| |
| titleCell:wikitext(navbar{
| |
| [1] = args['name'],
| |
| ['mini'] = 1,
| |
| })
| |
| end
| |
|
| |
| end
| |
|
| |
| local function renderTitleRow(tbl)
| |
| if not args['title'] then return end
| |
|
| |
| local titleRow = tbl:tag('tr')
| |
|
| |
| local titleCell = titleRow:tag('th'):attr('scope', 'col')
| |
|
| |
| local titleColspan = 2
| |
| if args['imageleft'] then titleColspan = titleColspan + 1 end
| |
| if args['image'] then titleColspan = titleColspan + 1 end
| |
|
| |
| titleCell
| |
| :addClass('navbox-title')
| |
| :attr('colspan', titleColspan)
| |
|
| |
| renderNavBar(titleCell)
| |
|
| |
| titleCell
| |
| :tag('div')
| |
| -- id for aria-labelledby attribute
| |
| :attr('id', mw.uri.anchorEncode(args['title']))
| |
| :addClass('navbox-title-text')
| |
| :wikitext(processItem(args['title']))
| |
|
| |
| tbl:tag('tr')
| |
| :addClass('navbox-spacer')
| |
| end
| |
|
| |
| 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
| |
|
| |
| tbl:tag('tr')
| |
| :tag('td')
| |
| :addClass('navbox-abovebelow')
| |
| :attr('colspan', getAboveBelowColspan())
| |
| :tag('div')
| |
| -- id for aria-labelledby attribute, if no title
| |
| :attr('id', args['title'] and nil or mw.uri.anchorEncode(args['above']))
| |
| :wikitext(processItem(args['above'], args['nowrapitems']))
| |
| | | |
| tbl:tag('tr') | | table.insert( navbox, '|}' ) |
| :addClass('navbox-spacer')
| |
| end
| |
| | |
| local function renderBelowRow(tbl)
| |
| if not args['below'] then return end
| |
| | | |
| tbl:tag('tr') | | navbox = table.concat( navbox, '\n' ):gsub( ' style=""', '' ) |
| :addClass('navbox-spacer')
| | return navbox |
|
| |
| tbl:tag('tr')
| |
| :tag('td')
| |
| :addClass('navbox-abovebelow')
| |
| :attr('colspan', getAboveBelowColspan())
| |
| :tag('div')
| |
| :wikitext(processItem(args['below'], args['nowrapitems']))
| |
| end | | end |
|
| |
| local function renderListRow(tbl, index, listnum, listnums_size)
| |
| if index > 1 then
| |
| tbl:tag('tr')
| |
| :addClass('navbox-spacer')
| |
| end
| |
|
| |
| local row = tbl:tag('tr')
| |
|
| |
| if index == 1 and args['imageleft'] then
| |
| row
| |
| :tag('td')
| |
| :addClass('noviewer')
| |
| :addClass('navbox-image')
| |
| :css('width', '1px') -- Minimize width
| |
| :css('padding', '0 2px 0 0')
| |
| :attr('rowspan', listnums_size)
| |
| :tag('div')
| |
| :wikitext(processItem(args['imageleft']))
| |
| end
| |
|
| |
| local group_and_num = format('group%d', listnum)
| |
| if args[group_and_num] then
| |
| local groupCell = row:tag('th')
| |
|
| |
| -- id for aria-labelledby attribute, if lone group with no title or above
| |
| if listnum == 1 and not (args['title'] or args['above'] or args['group2']) then
| |
| groupCell
| |
| :attr('id', mw.uri.anchorEncode(args['group1']))
| |
| end
| |
|
| |
| groupCell
| |
| :attr('scope', 'row')
| |
| :addClass('navbox-group')
| |
|
| |
| groupCell
| |
| :wikitext(args[group_and_num])
| |
| end
| |
|
| |
| local listCell = row:tag('td')
| |
|
| |
| if args[group_and_num] then
| |
| listCell
| |
| :addClass('navbox-list-with-group')
| |
| else
| |
| listCell:attr('colspan', 2)
| |
| end
| |
|
| |
| local list_and_num = format('list%d', listnum)
| |
| local listText = args[list_and_num]
| |
| local oddEven = '\127_ODDEVEN_\127'
| |
| if listText:sub(1, 12) == '</div><table' then
| |
| -- Assume list text is for a subgroup navbox so no automatic striping for this row.
| |
| oddEven = listText:find('<th[^>]*"navbox%-title"') and '\127_ODDEVEN0_\127' or 'odd'
| |
| end
| |
|
| |
| local listclass_and_num = format('list%dclass', listnum)
| |
| listCell
| |
| :addClass('navbox-list')
| |
| :addClass('navbox-' .. oddEven)
| |
| :addClass(args['listclass'])
| |
| :addClass(args[listclass_and_num])
| |
| :tag('div')
| |
| :wikitext(processItem(listText, args['nowrapitems']))
| |
|
| |
| if index == 1 and args['image'] then
| |
| row
| |
| :tag('td')
| |
| :addClass('noviewer')
| |
| :addClass('navbox-image')
| |
| :css('width', '1px') -- Minimize width
| |
| :css('padding', '0 0 0 2px')
| |
| :attr('rowspan', listnums_size)
| |
| :tag('div')
| |
| :wikitext(processItem(args['image']))
| |
| end
| |
| end
| |
|
| |
|
| |
| local function renderMainTable(border, listnums)
| |
| local tbl = mw.html.create('table')
| |
| :addClass('nowraplinks')
| |
|
| |
| local state = args['state']
| |
| if args['title'] and state ~= 'plain' and state ~= 'off' then
| |
| if state == 'collapsed' then
| |
| state = 'mw-collapsed'
| |
| end
| |
| tbl
| |
| :addClass('mw-collapsible')
| |
| :addClass(state or 'autocollapse')
| |
| end
| |
|
| |
| if border == 'subgroup' or border == 'none' then
| |
| tbl
| |
| :addClass('navbox-subgroup')
| |
| else -- regular navbox
| |
| tbl
| |
| :addClass('navbox-inner')
| |
| end
| |
|
| |
| renderTitleRow(tbl)
| |
| renderAboveRow(tbl)
| |
| local listnums_size = #listnums
| |
| for i, listnum in ipairs(listnums) do
| |
| renderListRow(tbl, i, listnum, listnums_size)
| |
| end
| |
| renderBelowRow(tbl)
| |
|
| |
| return tbl
| |
| end
| |
|
| |
| function p._navbox(navboxArgs)
| |
| args = navboxArgs
| |
| local listnums = {}
| |
|
| |
| for k, _ in pairs(args) do
| |
| if type(k) == 'string' then
| |
| local listnum = k:match('^list(%d+)$')
| |
| if listnum then table.insert(listnums, tonumber(listnum)) end
| |
| end
| |
| end
| |
| table.sort(listnums)
| |
|
| |
| local border = mw.text.trim(args['border'] or args[1] or '')
| |
| if border == 'child' then
| |
| border = 'subgroup'
| |
| end
| |
|
| |
| -- render the main body of the navbox
| |
| local tbl = renderMainTable(border, listnums)
| |
|
| |
| local res = mw.html.create()
| |
| -- render the appropriate wrapper for the navbox, based on the border param
| |
|
| |
| if border == 'none' then
| |
| local nav = res:tag('div')
| |
| :attr('role', 'navigation')
| |
| :node(tbl)
| |
| -- aria-labelledby title, otherwise above, otherwise lone group
| |
| if args['title'] or args['above'] or (args['group1']
| |
| and not args['group2']) then
| |
| nav:attr(
| |
| 'aria-labelledby',
| |
| mw.uri.anchorEncode(
| |
| args['title'] or args['above'] or args['group1']
| |
| )
| |
| )
| |
| else
| |
| nav:attr('aria-label', 'Navbox')
| |
| end
| |
| elseif border == 'subgroup' 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>')
| |
| :node(tbl)
| |
| :wikitext('<div>')
| |
| else
| |
| local nav = res:tag('div')
| |
| :attr('role', 'navigation')
| |
| :addClass('navbox')
| |
| :addClass(args['class'])
| |
| :node(tbl)
| |
| -- aria-labelledby title, otherwise above, otherwise lone group
| |
| if args['title'] or args['above']
| |
| or (args['group1'] and not args['group2']) then
| |
| nav:attr(
| |
| 'aria-labelledby',
| |
| mw.uri.anchorEncode(args['title'] or args['above'] or args['group1'])
| |
| )
| |
| else
| |
| nav:attr('aria-label', 'Navbox')
| |
| end
| |
| end
| |
|
| |
| return striped(tostring(res), border)
| |
| end
| |
|
| |
| function p.navbox(frame)
| |
| if not getArgs then
| |
| getArgs = require('Module:ArgsUtil').merge
| |
| end
| |
| args = getArgs()
| |
|
| |
| -- Read the arguments in the order they'll be output in, to make references
| |
| -- number in the right order.
| |
| local _
| |
| _ = args['title']
| |
| _ = args['above']
| |
| -- Limit this to 20 as covering 'most' cases (that's a SWAG) and because
| |
| -- iterator approach won't work here
| |
| for i = 1, 20 do
| |
| _ = args[format('group%d', i)]
| |
| _ = args[format('list%d', i)]
| |
| end
| |
| _ = args['below']
| |
|
| |
| return p._navbox(args)
| |
| end
| |
|
| |
| return p | | return p |