|
|
| Line 1: |
Line 1: |
| -- I don't remember exactly how this works, I just remember this being a
| |
| -- frankenstien of things on the Minecraft and Terraria Wiki Navbox
| |
|
| |
|
| local p = {}
| |
| local getArgs -- lazily initialized
| |
| local args
| |
| local format = string.format
| |
|
| |
| local function get_title_arg(is_collapsible, template)
| |
| local title_arg = 1
| |
| if is_collapsible then title_arg = 2 end
| |
| if template then title_arg = 'template' end
| |
| return title_arg
| |
| 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
| |
| div:done()
| |
| :tag('div')
| |
| :addClass(title_text_class)
| |
| :wikitext(args[1])
| |
| end
| |
|
| |
| 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
| |
| local changer
| |
| if first == second then
| |
| changer = first
| |
| else
| |
| 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
| |
| index = index + 1
| |
| return index % 2 == 1 and first or second
| |
| end
| |
| end
| |
| return (wikitext:gsub('\127_ODDEVEN(%d?)_\127', changer)) -- () omits gsub count
| |
| end
| |
|
| |
| local function processItem(item, nowrapitems)
| |
| if item:sub(1, 2) == '{|' then
| |
| -- Applying nowrap to lines in a table does not make sense.
| |
| -- Add newlines to compensate for trim of x in |parm=x in a template.
| |
| 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
| |
| table.insert(lines, line)
| |
| end
| |
| item = table.concat(lines, '\n')
| |
| end
| |
| if item:match('^[*:;#]') then
| |
| return '\n' .. item ..'\n'
| |
| 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')
| |
| :addClass('navbox-spacer')
| |
| end
| |
|
| |
| local function renderBelowRow(tbl)
| |
| if not args['below'] then return end
| |
|
| |
| tbl:tag('tr')
| |
| :addClass('navbox-spacer')
| |
|
| |
| tbl:tag('tr')
| |
| :tag('td')
| |
| :addClass('navbox-abovebelow')
| |
| :attr('colspan', getAboveBelowColspan())
| |
| :tag('div')
| |
| :wikitext(processItem(args['below'], args['nowrapitems']))
| |
| 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
| |