(Created page with "-- -- This module implements {{Navbox}} -- local p = {} local navbar = require('Module:Navbar')._navbar local getArgs -- lazily initialized local args local border local li...") |
No edit summary |
||
Line 1: | Line 1: | ||
+ | -------------------------------------------------------------------- |
||
+ | --<pre> Navbox Module |
||
-- |
-- |
||
+ | -- * Fully CSS styled (inline styles possible but not default) |
||
− | -- This module implements {{Navbox}} |
||
+ | -- * Supports unlimited rows |
||
-- |
-- |
||
+ | -- By User:Tjcool007 from layton.wikia.com |
||
− | |||
+ | -------------------------------------------------------------------- |
||
+ | |||
local p = {} |
local p = {} |
||
+ | |||
− | |||
+ | local args = {} -- Arguments passed to template |
||
− | local navbar = require('Module:Navbar')._navbar |
||
− | local |
+ | local navbox -- Actual navbox |
+ | |||
− | |||
− | local |
+ | --local working = {} |
+ | local rownums, skiprows = {}, {} |
||
− | local border |
||
+ | local hasrows, alt, hasData, isChild = false, false, false, false |
||
− | local listnums = {} |
||
+ | local activeSection, sections, cimage, cimageleft |
||
− | local ODD_EVEN_MARKER = '\127_ODDEVEN_\127' |
||
+ | local colspan, rowspan |
||
− | local RESTART_MARKER = '\127_ODDEVEN0_\127' |
||
+ | |||
− | local REGEX_MARKER = '\127_ODDEVEN(%d?)_\127' |
||
+ | local showText, hideText = 'Show', 'Hide' |
||
− | |||
+ | |||
− | local function striped(wikitext) |
||
+ | local langCode = mw.getContentLanguage():getCode() |
||
− | -- Return wikitext with markers replaced for odd/even striping. |
||
+ | local localization = {} --localized strings table |
||
− | -- Child (subgroup) navboxes are flagged with a category that is removed |
||
+ | localization['en'] = {show = 'Show', hide = 'Hide'} |
||
− | -- by parent navboxes. The result is that the category shows all pages |
||
+ | localization['ru'] = {show = 'показать', hide = 'скрыть'} |
||
− | -- where a child navbox is not contained in a parent navbox. |
||
+ | if localization[langCode] then |
||
− | local orphanCat = '[[Category:Navbox orphans]]' |
||
+ | showText = localization[langCode]['show'] |
||
− | if border == 'subgroup' and args.orphan ~= 'yes' then |
||
+ | hideText = localization[langCode]['hide'] |
||
− | -- No change; striping occurs in outermost navbox. |
||
+ | end |
||
− | return wikitext .. orphanCat |
||
+ | |||
+ | ------------------------------------------------ |
||
+ | -- Title |
||
+ | ------------------------------------------------ |
||
+ | |||
+ | --- Processes the VDE links in the title |
||
+ | -- |
||
+ | -- @param titlecell The table cell of the title |
||
+ | local function processVde( titlecell ) |
||
+ | if not args.template then return end |
||
+ | |||
+ | titlecell:wikitext('<span class="navbox-vde">' |
||
+ | .. mw.getCurrentFrame():expandTemplate({ |
||
+ | title = 'vdelinks', |
||
+ | args = { args.template, ['type'] = 'navbox' } |
||
+ | }) .. '</span>') |
||
+ | end |
||
+ | |||
+ | --- Processes the main title row |
||
+ | local function processTitle() |
||
+ | local titlerow = mw.html.create('tr'):addClass('navbox-title') |
||
+ | local titlecell = mw.html.create('th'):attr('colspan',colspan):attr('scope','col') |
||
+ | |||
+ | if not pcall( processVde, titlecell ) then |
||
+ | titlecell:wikitext( '<b class="navbox-vde error" title="Missing Template:Vdelinks">!!!</b>' ) |
||
end |
end |
||
+ | |||
− | local first, second = 'odd', 'even' |
||
+ | titlecell:wikitext( args.title or '{{{title}}}' ) |
||
− | if args.evenodd then |
||
+ | |||
− | if args.evenodd == 'swap' then |
||
+ | -- Padding |
||
− | first, second = second, first |
||
+ | local hasTemplate = args.template ~= nil |
||
+ | local hasState = not args.state or args.state ~= 'plain' |
||
+ | |||
+ | if hasTemplate ~= hasState then |
||
+ | if hasTemplate then |
||
+ | titlecell:addClass('navbox-title-padright') |
||
else |
else |
||
+ | titlecell:addClass('navbox-title-padleft') |
||
− | first = args.evenodd |
||
− | second = first |
||
end |
end |
||
end |
end |
||
+ | |||
− | local changer |
||
+ | if args.titleclass then titlerow:addClass( args.titleclass ) end |
||
− | if first == second then |
||
+ | if args.titlestyle then titlecell:cssText( args.titlestyle ) end |
||
− | changer = first |
||
+ | |||
− | else |
||
+ | titlerow:node(titlecell) |
||
− | local index = 0 |
||
+ | navbox:node(titlerow) |
||
− | changer = function (code) |
||
+ | end |
||
− | if code == '0' then |
||
+ | |||
− | -- Current occurrence is for a group before a nested table. |
||
+ | local function _addGutter( parent, incRowspan ) |
||
− | -- Set it to first as a valid although pointless class. |
||
+ | parent:tag('tr'):addClass('navbox-gutter'):tag('td'):attr('colspan',2) |
||
− | -- The next occurrence will be the first row after a title |
||
+ | |||
− | -- in a subgroup and will also be first. |
||
+ | if incRowspan then |
||
− | index = 0 |
||
+ | rowspan = rowspan + 1 |
||
− | return first |
||
− | end |
||
− | index = index + 1 |
||
− | return index % 2 == 1 and first or second |
||
− | end |
||
end |
end |
||
− | local regex = orphanCat:gsub('([%[%]])', '%%%1') |
||
− | return (wikitext:gsub(regex, ''):gsub(REGEX_MARKER, changer)) -- () omits gsub count |
||
end |
end |
||
+ | |||
− | |||
+ | ------------------------------------------------ |
||
− | local function processItem(item, nowrapitems) |
||
+ | -- Above/Below |
||
− | 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. |
||
+ | --- Processes the above and below rows |
||
− | return '\n' .. item ..'\n' |
||
+ | -- |
||
+ | -- @param rowtype Either 'above' or 'below' |
||
+ | local function processAboveBelow( rowtype ) |
||
+ | if not args[rowtype] then return end |
||
+ | |||
+ | local abrow = mw.html.create('tr'):addClass('navbox-'..rowtype) |
||
+ | local abcell = mw.html.create('td'):attr('colspan',colspan):wikitext( args[rowtype] ) |
||
+ | |||
+ | 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 |
||
+ | |||
+ | ------------------------------------------------ |
||
+ | -- Main Rows |
||
+ | ------------------------------------------------ |
||
+ | |||
+ | --- Processes the images |
||
+ | local function _processImage(row, imgtype) |
||
+ | if not args[imgtype] then return end |
||
+ | |||
+ | local iclass = imgtype == 'image' and 'navbox-image-right' or 'navbox-image-left' |
||
+ | |||
+ | local imagecell = mw.html.create('td'):addClass('navbox-image'):addClass(iclass) |
||
+ | |||
+ | 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','0%'):wikitext(image) |
||
end |
end |
||
+ | |||
− | if nowrapitems == 'yes' then |
||
+ | if args[imgtype .. 'class'] then imagecell:addClass( args[imgtype .. 'class'] ) end |
||
− | local lines = {} |
||
+ | if args[imgtype .. 'style'] then imagecell:cssText( args[imgtype .. 'style'] ) end |
||
− | for line in (item .. '\n'):gmatch('([^\n]*)\n') do |
||
+ | |||
− | local prefix, content = line:match('^([*:;#]+)%s*(.*)') |
||
+ | row:node( imagecell ) |
||
− | if prefix and not content:match('^<span class="nowrap">') then |
||
+ | if imgtype == 'image' then |
||
− | line = prefix .. '<span class="nowrap">' .. content .. '</span>' |
||
+ | cimage = imagecell |
||
− | end |
||
+ | else |
||
− | table.insert(lines, line) |
||
+ | cimageleft = imagecell |
||
− | end |
||
− | item = table.concat(lines, '\n') |
||
end |
end |
||
+ | end |
||
− | if item:match('^[*:;#]') then |
||
+ | |||
− | return '\n' .. item ..'\n' |
||
+ | --- Closes the currently active section (if any) |
||
+ | local function _closeCurrentSection() |
||
+ | if not activeSection then return end |
||
+ | |||
+ | local row = mw.html.create('tr'):addClass('navbox-section-row') |
||
+ | local cell = mw.html.create('td'):attr('colspan',2) |
||
+ | |||
+ | if not hasrows then |
||
+ | _processImage(row,'imageleft') |
||
end |
end |
||
+ | |||
− | return item |
||
+ | cell:node(sections[activeSection]) |
||
+ | 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 |
||
− | local function renderNavBar(titleCell) |
||
+ | -- |
||
− | |||
+ | -- @return Alternatingly returns true or false. Always returns false if alternating rows |
||
− | if args.navbar ~= 'off' and args.navbar ~= 'plain' and not (not args.name and mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', '') == 'Template:Navbox') then |
||
+ | -- are disabled with "alternaterows = no" |
||
− | titleCell:wikitext(navbar{ |
||
+ | local function _alternateRow() |
||
− | args.name, |
||
+ | if args.alternaterows == 'no' then return false end |
||
− | mini = 1, |
||
+ | if alt then |
||
− | fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') .. ';background:none transparent;border:none;-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none;' |
||
+ | alt = false |
||
− | }) |
||
+ | return true |
||
+ | else |
||
+ | alt = true |
||
+ | return false |
||
end |
end |
||
− | |||
end |
end |
||
+ | |||
− | |||
+ | --- Process a single Header "row" |
||
-- |
-- |
||
+ | -- @param num Number of the row to be processed |
||
− | -- Title row |
||
+ | local function processHeader(num) |
||
− | -- |
||
+ | if not args['header'..num] then return end |
||
− | local function renderTitleRow(tbl) |
||
+ | |||
− | if not args.title then return end |
||
+ | _closeCurrentSection() |
||
− | |||
+ | |||
− | local titleRow = tbl:tag('tr') |
||
+ | local subtable = mw.html.create('table'):addClass('navbox-section') |
||
− | |||
+ | local headerrow = mw.html.create('tr') |
||
− | if args.titlegroup then |
||
+ | local header = mw.html.create('th'):addClass('navbox-header'):attr('colspan',2):attr('scope','col'):wikitext( args['header'..num] ) |
||
− | titleRow |
||
+ | |||
− | :tag('th') |
||
+ | local collapseme = args['state'..num] or false |
||
− | :attr('scope', 'row') |
||
+ | local state = false |
||
− | :addClass('navbox-group') |
||
+ | |||
− | :addClass(args.titlegroupclass) |
||
+ | if collapseme then |
||
− | :cssText(args.basestyle) |
||
+ | -- Look at this one |
||
− | :cssText(args.groupstyle) |
||
+ | if collapseme ~= 'plain' then |
||
− | :cssText(args.titlegroupstyle) |
||
+ | state = collapseme == 'expanded' and 'expanded' or 'collapsed' |
||
− | :wikitext(args.titlegroup) |
||
+ | end |
||
+ | else |
||
+ | -- Look at default |
||
+ | local collapseall = args.defaultstate or false |
||
+ | if collapseall then |
||
+ | state = collapseall == 'expanded' and 'expanded' or 'collapsed' |
||
+ | end |
||
end |
end |
||
+ | |||
− | |||
+ | if state then |
||
− | local titleCell = titleRow:tag('th'):attr('scope', 'col') |
||
+ | 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) |
||
− | |||
− | + | if state == 'collapsed' then |
|
+ | subtable:addClass('mw-collapsed') |
||
− | titleCell |
||
+ | end |
||
− | :css('border-left', '1px solid #fdfdfd') |
||
+ | header:addClass('navbox-header-collapsible') |
||
− | :css('width', '100%') |
||
end |
end |
||
+ | |||
− | |||
+ | if args.headerclass then headerrow:addClass( args.headerclass ) end |
||
− | local titleColspan = 2 |
||
− | if args. |
+ | if args.headerstyle then header:cssText( args.headerstyle ) end |
+ | |||
− | if args.image then titleColspan = titleColspan + 1 end |
||
+ | headerrow:node(header) |
||
− | if args.titlegroup then titleColspan = titleColspan - 1 end |
||
+ | subtable:node(headerrow) |
||
− | |||
+ | |||
− | titleCell |
||
+ | sections[num] = subtable |
||
− | :cssText(args.basestyle) |
||
+ | activeSection = num |
||
− | :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%') |
||
− | :css('margin', '0 4em') |
||
− | :wikitext(processItem(args.title)) |
||
end |
end |
||
+ | |||
− | |||
+ | --- Processes a single list row |
||
-- |
-- |
||
+ | -- @param num Number of the row to be processed |
||
− | -- Above/Below rows |
||
+ | local function processList(num) |
||
− | -- |
||
+ | if not args['list'..num] then return end |
||
− | |||
+ | |||
− | local function getAboveBelowColspan() |
||
+ | local row = mw.html.create('tr'):addClass('navbox-row') |
||
− | local ret = 2 |
||
+ | |||
− | if args.imageleft then ret = ret + 1 end |
||
+ | if not hasrows and not activeSection then |
||
− | if args.image then ret = ret + 1 end |
||
+ | _processImage(row, 'imageleft') |
||
− | return ret |
||
− | end |
||
− | |||
− | local function renderAboveRow(tbl) |
||
− | if not args.above then return end |
||
− | |||
− | tbl:tag('tr') |
||
− | :tag('td') |
||
− | :addClass('navbox-abovebelow') |
||
− | :addClass(args.aboveclass) |
||
− | :cssText(args.basestyle) |
||
− | :cssText(args.abovestyle) |
||
− | :attr('colspan', getAboveBelowColspan()) |
||
− | :tag('div') |
||
− | :wikitext(processItem(args.above, args.nowrapitems)) |
||
− | end |
||
− | |||
− | local function renderBelowRow(tbl) |
||
− | if not args.below then return end |
||
− | |||
− | tbl:tag('tr') |
||
− | :tag('td') |
||
− | :addClass('navbox-abovebelow') |
||
− | :addClass(args.belowclass) |
||
− | :cssText(args.basestyle) |
||
− | :cssText(args.belowstyle) |
||
− | :attr('colspan', getAboveBelowColspan()) |
||
− | :tag('div') |
||
− | :wikitext(processItem(args.below, args.nowrapitems)) |
||
− | end |
||
− | |||
− | -- |
||
− | -- List rows |
||
− | -- |
||
− | local function renderListRow(tbl, index, listnum) |
||
− | local row = tbl:tag('tr') |
||
− | |||
− | if index == 1 and args.imageleft then |
||
− | row |
||
− | :tag('td') |
||
− | :addClass('navbox-image') |
||
− | :addClass(args.imageclass) |
||
− | :css('width', '1px') -- Minimize width |
||
− | :css('padding', '0px 2px 0px 0px') |
||
− | :cssText(args.imageleftstyle) |
||
− | :attr('rowspan', #listnums) |
||
− | :tag('div') |
||
− | :wikitext(processItem(args.imageleft)) |
||
end |
end |
||
+ | |||
− | |||
+ | local listcell = mw.html.create('td'):addClass('navbox-list') |
||
− | if args['group' .. listnum] then |
||
− | + | local hlistcell = listcell:tag('div'):addClass('hlist') |
|
+ | |||
− | |||
+ | local data = args['list'..num] |
||
− | groupCell |
||
+ | |||
− | :attr('scope', 'row') |
||
+ | if data:sub(1,1) == '*' then |
||
− | :addClass('navbox-group') |
||
+ | -- Add newlines to support lists properly |
||
− | :addClass(args.groupclass) |
||
+ | hlistcell |
||
− | :cssText(args.basestyle) |
||
+ | :newline() |
||
− | :css('width', args.groupwidth or '1%') -- If groupwidth not specified, minimize width |
||
+ | :wikitext( data ) |
||
− | |||
+ | :newline() |
||
− | groupCell |
||
+ | else |
||
− | :cssText(args.groupstyle) |
||
+ | hlistcell:wikitext( data ) |
||
− | :cssText(args['group' .. listnum .. 'style']) |
||
− | :wikitext(args['group' .. listnum]) |
||
end |
end |
||
+ | |||
− | |||
− | local |
+ | local altRow = _alternateRow() |
+ | if altRow then |
||
− | |||
+ | row:addClass( args.altrowclass or 'alt' ) |
||
− | if args['group' .. listnum] then |
||
+ | |||
− | listCell |
||
+ | local listclass = args.altlistclass or args.listclass or false |
||
− | :css('text-align', 'left') |
||
+ | if listclass then listcell:addClass( listclass ) end |
||
− | :css('border-left-width', '1px') |
||
+ | |||
− | :css('border-left-style', 'solid') |
||
+ | local liststyle = args.altliststyle or args.liststyle or false |
||
+ | if liststyle then listcell:cssText( liststyle ) end |
||
else |
else |
||
+ | if args.rowclass then row:addClass( args.rowclass ) end |
||
− | listCell:attr('colspan', 2) |
||
+ | if args.listclass then listcell:addClass( args.listclass ) end |
||
+ | if args.liststyle then listcell:cssText( args.liststyle ) end |
||
end |
end |
||
+ | |||
− | |||
− | if |
+ | if args['group'..num] then |
+ | local groupcell = mw.html.create('th'):addClass('navbox-group'):attr('scope','row'):wikitext( args['group'..num] ) |
||
− | listCell:css('width', '100%') |
||
− | + | ||
+ | if altRow then |
||
− | |||
+ | local groupclass = args.altgroupclass or args.groupclass or false |
||
− | local rowstyle -- usually nil so cssText(rowstyle) usually adds nothing |
||
+ | if groupclass then groupcell:addClass( groupclass ) end |
||
− | if index % 2 == 1 then |
||
+ | |||
− | rowstyle = args.oddstyle |
||
+ | 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 |
else |
||
+ | listcell:attr('colspan',2):addClass('no-group') |
||
− | rowstyle = args.evenstyle |
||
end |
end |
||
+ | |||
− | |||
+ | row:node( listcell ) |
||
− | local listText = args['list' .. listnum] |
||
+ | |||
− | local oddEven = ODD_EVEN_MARKER |
||
+ | local firstRow = false |
||
− | if listText:sub(1, 12) == '</div><table' then |
||
+ | if not hasrows and not activeSection then |
||
− | -- Assume list text is for a subgroup navbox so no automatic striping for this row. |
||
+ | firstRow = true |
||
− | oddEven = listText:find('<th[^>]*"navbox%-title"') and RESTART_MARKER or 'odd' |
||
+ | hasrows = true |
||
+ | _processImage(row, 'image') |
||
end |
end |
||
+ | |||
− | listCell |
||
+ | if activeSection then |
||
− | :css('padding', '0px') |
||
+ | local parent = sections[activeSection] |
||
− | :cssText(args.liststyle) |
||
+ | if not isChild or not firstRow then |
||
− | :cssText(rowstyle) |
||
+ | _addGutter(parent) |
||
− | :cssText(args['list' .. listnum .. 'style']) |
||
− | :addClass('navbox-list') |
||
− | :addClass('navbox-' .. oddEven) |
||
− | :addClass(args.listclass) |
||
− | :tag('div') |
||
− | :css('padding', (index == 1 and args.list1padding) or args.listpadding or '0em 0.25em') |
||
− | :wikitext(processItem(listText, args.nowrapitems)) |
||
− | |||
− | if index == 1 and args.image then |
||
− | row |
||
− | :tag('td') |
||
− | :addClass('navbox-image') |
||
− | :addClass(args.imageclass) |
||
− | :css('width', '1px') -- Minimize width |
||
− | :css('padding', '0px 0px 0px 1px') |
||
− | :cssText(args.imagestyle) |
||
− | :attr('rowspan', #listnums) |
||
− | :tag('div') |
||
− | :wikitext(processItem(args.image)) |
||
− | end |
||
− | end |
||
− | |||
− | |||
− | -- |
||
− | -- Tracking categories |
||
− | -- |
||
− | |||
− | local function needsHorizontalLists() |
||
− | if border == 'subgroup' or args.tracking == 'no' then |
||
− | return false |
||
− | end |
||
− | local listClasses = { |
||
− | ['plainlist'] = true, ['hlist'] = true, ['hlist hnum'] = true, |
||
− | ['hlist hwrap'] = true, ['hlist vcard'] = true, ['vcard hlist'] = true, |
||
− | ['hlist vevent'] = true, |
||
− | } |
||
− | return not (listClasses[args.listclass] or listClasses[args.bodyclass]) |
||
− | end |
||
− | |||
− | local function hasBackgroundColors() |
||
− | for _, key in ipairs({'titlestyle', 'groupstyle', 'basestyle'}) do |
||
− | if tostring(args[key]):find('background', 1, true) then |
||
− | return true |
||
end |
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 |
||
end |
end |
||
+ | |||
− | |||
+ | --- Processes all rows |
||
− | local function isIllegible() |
||
+ | local function processRows() |
||
− | local styleratio = require('Module:Color contrast')._styleratio |
||
+ | sections = {} |
||
− | |||
− | for |
+ | for i=1,#rownums do |
+ | local num = rownums[i] |
||
− | if tostring(key):match("style$") then |
||
+ | if not skiprows[num] then |
||
− | if styleratio{mw.text.unstripNoWiki(style)} < 4.5 then |
||
+ | processHeader(num) |
||
− | return true |
||
+ | processList(num) |
||
− | end |
||
end |
end |
||
end |
end |
||
+ | _closeCurrentSection() |
||
− | return false |
||
+ | |||
− | end |
||
+ | if cimageleft then |
||
− | |||
+ | cimageleft:attr('rowspan',rowspan) |
||
− | local function getTrackingCategories() |
||
+ | end |
||
− | local cats = {} |
||
+ | if cimage then |
||
− | if needsHorizontalLists() then table.insert(cats, 'Navigational boxes without horizontal lists') end |
||
+ | cimage:attr('rowspan',rowspan) |
||
− | if hasBackgroundColors() then table.insert(cats, 'Navboxes using background colours') end |
||
− | if isIllegible() then table.insert(cats, 'Potentially illegible navboxes') 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 _, cat in ipairs(getTrackingCategories()) do |
||
− | builder:wikitext('[[Category:' .. cat .. ']]') |
||
end |
end |
||
end |
end |
||
+ | |||
− | |||
+ | ------------------------------------------------ |
||
+ | -- ARGUMENTS PREPROCESSOR |
||
+ | -- * 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. |
||
− | -- Main navbox tables |
||
+ | local function preProcessArgs(frame) |
||
− | -- |
||
− | local |
+ | local tmp = {} |
+ | |||
− | local tbl = mw.html.create('table') |
||
+ | if frame == mw.getCurrentFrame() then |
||
− | :addClass('nowraplinks') |
||
+ | tmp = frame:getParent().args |
||
− | :addClass(args.bodyclass) |
||
+ | else |
||
− | |||
+ | tmp = frame |
||
− | if args.title and (args.state ~= 'plain' and args.state ~= 'off') then |
||
− | tbl |
||
− | :addClass('mw-collapsible') |
||
− | :addClass(args.state or 'mw-autocollapse') |
||
end |
end |
||
+ | |||
− | |||
+ | -- Storage tables |
||
− | tbl:css('border-spacing', 0) |
||
+ | local nums = {} |
||
− | if border == 'subgroup' or border == 'none' then |
||
+ | |||
− | tbl |
||
+ | -- Loop over all the args |
||
− | :addClass('navbox-subgroup') |
||
+ | for k,v in pairs(tmp) do |
||
− | :cssText(args.bodystyle) |
||
+ | -- Skip empty args, which are useless |
||
− | :cssText(args.style) |
||
+ | if v ~= '' then |
||
− | else -- regular navbox - bodystyle and style will be applied to the wrapper table |
||
+ | local cat,num = tostring(k):match('^(%a+)([1-9]%d*)$') |
||
− | tbl |
||
+ | |||
− | :addClass('navbox-inner') |
||
+ | if cat == 'header' or cat == 'list' then |
||
− | :css('background', 'transparent') |
||
+ | nums[num] = true |
||
− | :css('color', 'inherit') |
||
− | + | end |
|
+ | |||
− | tbl:cssText(args.innerstyle) |
||
+ | args[k] = v -- Simple copy |
||
− | |||
− | renderTitleRow(tbl) |
||
− | renderAboveRow(tbl) |
||
− | for i, listnum in ipairs(listnums) do |
||
− | renderListRow(tbl, i, listnum) |
||
− | end |
||
− | renderBelowRow(tbl) |
||
− | |||
− | return tbl |
||
− | end |
||
− | |||
− | function p._navbox(navboxArgs) |
||
− | args = navboxArgs |
||
− | |||
− | 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 |
||
end |
end |
||
+ | |||
− | table.sort(listnums) |
||
+ | colspan = args.image and 3 or 2 |
||
− | |||
+ | if args.imageleft then colspan = colspan + 1 end |
||
− | border = mw.text.trim(args.border or args[1] or '') |
||
+ | rowspan = 0 |
||
− | if border == 'child' then |
||
+ | |||
− | border = 'subgroup' |
||
+ | if args.alternaterows == 'swap' then |
||
+ | alt = true |
||
end |
end |
||
+ | |||
− | |||
+ | for k, v in pairs(nums) do |
||
− | -- render the main body of the navbox |
||
+ | rownums[#rownums+1] = tonumber(k) |
||
− | local tbl = renderMainTable() |
||
+ | end |
||
− | |||
+ | |||
− | -- render the appropriate wrapper around the navbox, depending on the border param |
||
+ | table.sort(rownums) |
||
− | local res = mw.html.create() |
||
+ | |||
− | if border == 'none' then |
||
+ | -- Calculate skip rows |
||
− | local nav = res:tag('div') |
||
+ | local cSection, cSkip |
||
− | :attr('role', 'navigation') |
||
+ | local showall = args.showall |
||
− | :node(tbl) |
||
+ | for i=1,#rownums do |
||
− | if args.title then |
||
+ | local num = rownums[i] |
||
− | nav:attr('aria-labelledby', mw.uri.anchorEncode(args.title)) |
||
+ | if args['header'..num] then |
||
− | else |
||
+ | cSection = true |
||
− | nav:attr('aria-label', 'Navbox') |
||
+ | cSkip = false |
||
+ | 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 |
end |
||
− | + | if cSection and cSkip then |
|
+ | skiprows[num] = true |
||
− | -- 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') |
||
− | :cssText(args.bodystyle) |
||
− | :cssText(args.style) |
||
− | :css('padding', '2px') |
||
− | :node(tbl) |
||
− | if args.title then |
||
− | nav:attr('aria-labelledby', mw.uri.anchorEncode(args.title)) |
||
− | else |
||
− | nav:attr('aria-label', 'Navbox') |
||
end |
end |
||
end |
end |
||
− | |||
− | renderTrackingCategories(res) |
||
− | |||
− | return striped(tostring(res)) |
||
end |
end |
||
+ | |||
− | |||
+ | ------------------------------------------------ |
||
− | function p.navbox(frame) |
||
+ | -- MAIN FUNCTIONS |
||
− | if not getArgs then |
||
+ | ------------------------------------------------ |
||
− | getArgs = require('Module:Arguments').getArgs |
||
+ | |||
+ | --- Processes the arguments to create the navbox. |
||
+ | -- |
||
+ | -- @return A string with HTML that is the navbox. |
||
+ | local function _navbox() |
||
+ | -- Create the root HTML element |
||
+ | local trim = function(s) |
||
+ | return s and mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1") or '' |
||
end |
end |
||
+ | local border = args.border or trim(args[1]) or '' |
||
− | args = getArgs(frame, {wrappers = 'Template:Navbox'}) |
||
+ | isChild = (border == 'child' or border == 'subgroup') |
||
− | |||
+ | |||
− | -- Read the arguments in the order they'll be output in, to make references number in the right order. |
||
+ | if isChild then |
||
− | local _ |
||
+ | navbox = mw.html.create('table'):addClass('navbox-subgroup') |
||
− | _ = args.title |
||
+ | else |
||
− | _ = args.above |
||
+ | navbox = mw.html.create('table'):addClass('navbox') |
||
− | for i = 1, 20 do |
||
+ | |||
− | _ = args["group" .. tostring(i)] |
||
+ | if args.state ~= 'plain' then |
||
− | _ = args["list" .. tostring(i)] |
||
+ | navbox:addClass('mw-collapsible'):attr('data-expandtext',args['expandtext'] or args['defaultexpandtext'] or showText):attr('data-collapsetext',args['collapsetext'] or args['defaultcollapsetext'] or hideText) |
||
+ | if args.state == 'collapsed' then |
||
+ | navbox:addClass('mw-collapsed') |
||
+ | end |
||
+ | end |
||
end |
end |
||
+ | |||
− | _ = args.below |
||
+ | if args.bodyclass then navbox:addClass(args.bodyclass) end |
||
− | |||
+ | if args.bodystyle then navbox:cssText(args.bodystyle) end |
||
− | return p._navbox(args) |
||
+ | |||
+ | -- Process... |
||
+ | if not isChild then |
||
+ | processTitle() |
||
+ | processAboveBelow('above') |
||
+ | processRows() |
||
+ | processAboveBelow('below') |
||
+ | |||
+ | return tostring(navbox) |
||
+ | else |
||
+ | processRows() |
||
+ | |||
+ | local wrapper = mw.html.create('') |
||
+ | wrapper:wikitext('</div>') |
||
+ | wrapper:node(navbox) |
||
+ | wrapper:wikitext('<div class="hlist">') |
||
+ | return tostring(wrapper) |
||
+ | end |
||
+ | end |
||
+ | |||
+ | --- Main module entry point. |
||
+ | -- To be called with {{#invoke:navbox|main}} or directly from another module. |
||
+ | -- |
||
+ | -- @param frame The frame passed to the module via the #invoke. If called from another |
||
+ | -- module directly, this should be a table with the parameter definition. |
||
+ | function p.main(frame) |
||
+ | -- Save the arguments in a local variable so other functions can use them. |
||
+ | preProcessArgs(frame) |
||
+ | |||
+ | return _navbox() |
||
end |
end |
||
+ | |||
− | |||
return p |
return p |
Latest revision as of 19:12, 25 February 2020
Documentation for this module may be created at Module:Navbox/doc
--------------------------------------------------------------------
--<pre> Navbox Module
--
-- * Fully CSS styled (inline styles possible but not default)
-- * Supports unlimited rows
--
-- By User:Tjcool007 from layton.wikia.com
--------------------------------------------------------------------
local p = {}
local args = {} -- Arguments passed to template
local navbox -- Actual navbox
--local working = {}
local rownums, skiprows = {}, {}
local hasrows, alt, hasData, isChild = false, false, false, false
local activeSection, sections, cimage, cimageleft
local colspan, rowspan
local showText, hideText = 'Show', 'Hide'
local langCode = mw.getContentLanguage():getCode()
local localization = {} --localized strings table
localization['en'] = {show = 'Show', hide = 'Hide'}
localization['ru'] = {show = 'показать', hide = 'скрыть'}
if localization[langCode] then
showText = localization[langCode]['show']
hideText = localization[langCode]['hide']
end
------------------------------------------------
-- Title
------------------------------------------------
--- Processes the VDE links in the title
--
-- @param titlecell The table cell of the title
local function processVde( titlecell )
if not args.template then return end
titlecell:wikitext('<span class="navbox-vde">'
.. mw.getCurrentFrame():expandTemplate({
title = 'vdelinks',
args = { args.template, ['type'] = 'navbox' }
}) .. '</span>')
end
--- Processes the main title row
local function processTitle()
local titlerow = mw.html.create('tr'):addClass('navbox-title')
local titlecell = mw.html.create('th'):attr('colspan',colspan):attr('scope','col')
if not pcall( processVde, titlecell ) then
titlecell:wikitext( '<b class="navbox-vde error" title="Missing Template:Vdelinks">!!!</b>' )
end
titlecell:wikitext( args.title or '{{{title}}}' )
-- Padding
local hasTemplate = args.template ~= nil
local hasState = not args.state or args.state ~= 'plain'
if hasTemplate ~= hasState then
if hasTemplate then
titlecell:addClass('navbox-title-padright')
else
titlecell:addClass('navbox-title-padleft')
end
end
if args.titleclass then titlerow:addClass( args.titleclass ) end
if args.titlestyle then titlecell:cssText( args.titlestyle ) end
titlerow:node(titlecell)
navbox:node(titlerow)
end
local function _addGutter( parent, incRowspan )
parent:tag('tr'):addClass('navbox-gutter'):tag('td'):attr('colspan',2)
if incRowspan then
rowspan = rowspan + 1
end
end
------------------------------------------------
-- Above/Below
------------------------------------------------
--- Processes the above and below rows
--
-- @param rowtype Either 'above' or 'below'
local function processAboveBelow( rowtype )
if not args[rowtype] then return end
local abrow = mw.html.create('tr'):addClass('navbox-'..rowtype)
local abcell = mw.html.create('td'):attr('colspan',colspan):wikitext( args[rowtype] )
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
------------------------------------------------
-- Main Rows
------------------------------------------------
--- Processes the images
local function _processImage(row, imgtype)
if not args[imgtype] then return end
local iclass = imgtype == 'image' and 'navbox-image-right' or 'navbox-image-left'
local imagecell = mw.html.create('td'):addClass('navbox-image'):addClass(iclass)
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','0%'):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
--- Closes the currently active section (if any)
local function _closeCurrentSection()
if not activeSection then return end
local row = mw.html.create('tr'):addClass('navbox-section-row')
local cell = mw.html.create('td'):attr('colspan',2)
if not hasrows then
_processImage(row,'imageleft')
end
cell:node(sections[activeSection])
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
--- Handles alternating rows
--
-- @return Alternatingly returns true or false. Always returns false if alternating 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 processHeader(num)
if not args['header'..num] then return end
_closeCurrentSection()
local subtable = mw.html.create('table'):addClass('navbox-section')
local headerrow = mw.html.create('tr')
local header = mw.html.create('th'):addClass('navbox-header'):attr('colspan',2):attr('scope','col'):wikitext( args['header'..num] )
local collapseme = args['state'..num] or false
local state = false
if collapseme then
-- Look at this one
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
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)
if state == 'collapsed' then
subtable:addClass('mw-collapsed')
end
header:addClass('navbox-header-collapsible')
end
if args.headerclass then headerrow:addClass( args.headerclass ) end
if args.headerstyle then header:cssText( args.headerstyle ) end
headerrow:node(header)
subtable:node(headerrow)
sections[num] = subtable
activeSection = num
end
--- Processes a single list row
--
-- @param num Number of the row to be processed
local function processList(num)
if not args['list'..num] then return end
local row = mw.html.create('tr'):addClass('navbox-row')
if not hasrows and not activeSection then
_processImage(row, 'imageleft')
end
local listcell = mw.html.create('td'):addClass('navbox-list')
local hlistcell = listcell:tag('div'):addClass('hlist')
local data = args['list'..num]
if data:sub(1,1) == '*' then
-- Add newlines to support lists properly
hlistcell
:newline()
:wikitext( data )
:newline()
else
hlistcell:wikitext( data )
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
--- Processes all rows
local function processRows()
sections = {}
for i=1,#rownums do
local num = rownums[i]
if not skiprows[num] then
processHeader(num)
processList(num)
end
end
_closeCurrentSection()
if cimageleft then
cimageleft:attr('rowspan',rowspan)
end
if cimage then
cimage:attr('rowspan',rowspan)
end
end
------------------------------------------------
-- ARGUMENTS PREPROCESSOR
-- * 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 preProcessArgs(frame)
local tmp = {}
if frame == mw.getCurrentFrame() then
tmp = frame:getParent().args
else
tmp = frame
end
-- Storage tables
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)
-- Calculate skip rows
local cSection, cSkip
local showall = args.showall
for i=1,#rownums do
local num = rownums[i]
if args['header'..num] then
cSection = true
cSkip = false
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
------------------------------------------------
-- MAIN FUNCTIONS
------------------------------------------------
--- Processes the arguments to create the navbox.
--
-- @return A string with HTML that is the navbox.
local function _navbox()
-- Create the root HTML element
local trim = function(s)
return s and mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1") or ''
end
local border = args.border or trim(args[1]) or ''
isChild = (border == 'child' or border == 'subgroup')
if isChild then
navbox = mw.html.create('table'):addClass('navbox-subgroup')
else
navbox = mw.html.create('table'):addClass('navbox')
if args.state ~= 'plain' then
navbox:addClass('mw-collapsible'):attr('data-expandtext',args['expandtext'] or args['defaultexpandtext'] or showText):attr('data-collapsetext',args['collapsetext'] or args['defaultcollapsetext'] or hideText)
if args.state == 'collapsed' then
navbox:addClass('mw-collapsed')
end
end
end
if args.bodyclass then navbox:addClass(args.bodyclass) end
if args.bodystyle then navbox:cssText(args.bodystyle) end
-- Process...
if not isChild then
processTitle()
processAboveBelow('above')
processRows()
processAboveBelow('below')
return tostring(navbox)
else
processRows()
local wrapper = mw.html.create('')
wrapper:wikitext('</div>')
wrapper:node(navbox)
wrapper:wikitext('<div class="hlist">')
return tostring(wrapper)
end
end
--- Main module entry point.
-- To be called with {{#invoke:navbox|main}} or directly from another module.
--
-- @param frame The frame passed to the module via the #invoke. If called from another
-- module directly, this should be a table with the parameter definition.
function p.main(frame)
-- Save the arguments in a local variable so other functions can use them.
preProcessArgs(frame)
return _navbox()
end
return p