Editing
Dev:NavBox
From TBATE Wiki
Jump to navigation
Jump to search
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
-------------------------------------------------------------------- --<pre> Navbox Module -- -- * Fully CSS styled (inline styles possible but not default) -- * Supports unlimited rows -- * Automatic striping alternation, including for child navbox subsections -- -- Original module by User:Tjcool007 from layton.fandom.com -------------------------------------------------------------------- local p = {} local i18n = require('Dev:i18n').loadMessages('Navbox') local showText, hideText = i18n:msg('show'), i18n:msg('hide') local cfg = { marker = { oddeven = '\127_ODDEVEN_\127', contains_subgroup = '\127_ODDEVEN0_\127', restart_header = '\127_ODDEVEN1_\127', regex_changer = '\127_ODDEVEN([01]?)_\127', odd = '\127_ODD_\127', even = '\127_EVEN_\127', oddeven_sep = '\127_ODDEVEN2_\127', oddeven_end = '\127_ODDEVEN3_\127', regex_oddeven_sep = '\127_ODDEVEN[23]_\127', regex_odd = '(\127_ODD_\127[^\127]*)\127_ODDEVEN2_\127([^\127]*)\127_ODDEVEN2_\127[^\127]*\127_ODDEVEN3_\127', regex_even = '(\127_EVEN_\127[^\127]*)\127_ODDEVEN2_\127[^\127]*\127_ODDEVEN2_\127([^\127]*)\127_ODDEVEN3_\127' }, pattern = { hlist = 'hlist', plainlist = 'plainlist', }, category = { error = '[[Category:Navboxes with internal errors]]', orphan = '[[Category:Navbox orphans]]' } } ------------------------------------------------ -- 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, args, rownums, skiprows = {}, {}, {}, {} if frame == mw.getCurrentFrame() then tmp = frame:getParent().args else tmp = frame end -- Storage tables local nums, subnums = {}, {} -- Loop over all the args for k, v in pairs(tmp) do -- Skip empty args, which are useless if v ~= '' then local str = tostring(k) local cat, num, subnum = str:match('^(%a+)([1-9]%d*)%.([1-9][%d%.]*)$') if subnum then nums[num] = true if type(subnums[num]) ~= "table" then subnums[num] = {} end subnums[num][cat..subnum] = v else cat, num = str:match('^(%a+)([1-9]%d*)$') if cat == 'header' or cat == 'list' then nums[num] = true end args[k] = v -- Simple copy end end end -- Generate child navboxes for k, v in pairs(subnums) do for _, arg in ipairs({'showall', 'alternaterows', 'titlestyle', 'titleclass', 'abovestyle', 'aboveclass', 'rowclass', 'altrowclass', 'groupstyle', 'groupclass', 'altgroupstyle', 'altgroupclass', 'liststyle', 'listclass', 'altliststyle', 'altlistclass', 'belowstyle', 'belowclass', 'defaultstate', 'defaultcollapsetext', 'defaultexpandtext'}) do v[arg] = args[arg] end v.border = 'child' args['list'..k] = p.main(v) 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 -- Insert markers into class and style parameters where needed for i, v in ipairs({'groupclass', 'groupstyle', 'listclass', 'liststyle'}) do if args['alt' .. v] then args[v] = cfg.marker.oddeven_sep .. (args[v] or '') .. cfg.marker.oddeven_sep .. args['alt' .. v] .. cfg.marker.oddeven_end end end return args, rownums, skiprows end ------------------------------------------------ -- MAIN FUNCTIONS ------------------------------------------------ --- Processes the arguments to create the navbox. -- -- @return A string with HTML that is the navbox. local function _navbox(args, rownums, skiprows) local navbox -- Actual navbox local hasrows, hasData, hasTitle, hasChild = false, false, false, false local activeSection, sections, cimage, cimageleft local border = args.border or mw.text.trim(args[1] or '') or '' if border == 'child' then border = 'subgroup' end local isChild = (border == 'subgroup') local colspan = args.image and 3 or 2 if args.imageleft then colspan = colspan + 1 end local rowspan = 0 --- Wraps text in newlines if it begins with a list or table local function processItem(item) if item:sub(1, 2) == '{|' then return '\n' .. item ..'\n' end if item:match('^[*:;#]') then return '\n' .. item ..'\n' end return item end --- Returns navbox contents with markers replaced for odd/even striping local function striped(wikitext, border) -- 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. local orphanCat = cfg.category.orphan if border == 'subgroup' and args.orphan ~= 'yes' then -- No change; striping occurs in outermost navbox. return wikitext .. orphanCat end local altrowclass = args.altrowclass or 'alt' altrowclass = ' ' .. altrowclass local first, second = '', altrowclass if args.alternaterows then if args.alternaterows == 'swap' then first, second = second, first elseif args.alternaterows == 'no' or args.alternaterows == 'odd' then second = '' elseif args.alternaterows == 'even' then first = altrowclass else first = args.alternaterows second = first end end local changer if first == second then changer = cfg.marker.odd else local index = 0 changer = function (code) if code == '0' then -- Subgroup encountered. -- Return a marker without incrementing the index. return index % 2 == 0 and cfg.marker.odd or cfg.marker.even elseif code == '1' then -- Title or header encountered. -- Restart the index and remove the marker. -- The next occurrence will use the initial class. index = 0 return '' end index = index + 1 return index % 2 == 1 and cfg.marker.odd or cfg.marker.even end end local regex = orphanCat:gsub('([%[%]])', '%%%1') wikitext = wikitext:gsub(regex, ''):gsub(cfg.marker.regex_changer, changer) local prevWikitext, hasNoMoreOddevenMarkers repeat prevWikitext = wikitext wikitext = wikitext:gsub(cfg.marker.regex_odd, '%1%2'):gsub(cfg.marker.regex_even, '%1%2') hasNoMoreOddevenMarkers = wikitext:match(cfg.marker.regex_oddeven_sep) == nil until hasNoMoreOddevenMarkers or wikitext == prevWikitext wikitext = wikitext:gsub(cfg.marker.odd, first):gsub(cfg.marker.even, second) if not hasNoMoreOddevenMarkers then -- Something went wrong. Flag for investigation. -- See <https://discord.com/channels/246075715714416641/1266361437484486778> for details. wikitext = wikitext .. cfg.category.error end return wikitext 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() if not hasTitle then return end local titlerow = mw.html.create('tr'):addClass('navbox-title'..cfg.marker.restart_header) local titlecell = mw.html.create('th'):attr('colspan',colspan):attr('scope','col') local titlediv = mw.html.create('div') if not pcall( processVde, titlecell ) then titlediv:wikitext( '<b class="navbox-vde error" title="Missing Template:Vdelinks">!!!</b>' ) end titlediv:wikitext( args.title or '{{{title}}}' ) -- Padding local hasTemplate = args.template ~= nil local hasState = not args.state or args.state ~= 'plain' if hasTemplate or hasState then -- remove redundant classes in future if it won't break the display of wikis' navboxes titlediv:addClass('navbox-title-pad navbox-title-padleft navbox-title-padright') end if args.titleclass then titlerow:addClass( args.titleclass ) end if args.titlestyle then titlecell:cssText( args.titlestyle ) end titlecell:node(titlediv) 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( processItem(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 ) if hasTitle or rowtype == 'below' then _addGutter( navbox ) end 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 if not isChild or not firstRow or hasTitle or args.above then _addGutter(navbox,not firstRow) end navbox:node(row) rowspan = rowspan + 1 activeSection = false hasData = false 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'..cfg.marker.restart_header):attr('colspan',2):attr('scope','col') local headerdiv = mw.html.create('div'):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 -- remove redundant classes in future if it won't break the display of wikis' navboxes headerdiv:addClass('navbox-header-pad navbox-title-padleft navbox-title-padright') end if args.headerclass then headerrow:addClass( args.headerclass ) end if args.headerstyle then header:cssText( args.headerstyle ) end header:node(headerdiv) headerrow:node(header) subtable:node(headerrow) sections[num] = subtable activeSection = num end -- Check if a string contains a particular CSS class local function hasListClass(htmlclass, arg) local patterns = { '^' .. htmlclass .. '$', '%s' .. htmlclass .. '$', '^' .. htmlclass .. '%s', '%s' .. htmlclass .. '%s' } for _, pattern in ipairs(patterns) do if mw.ustring.find(args[arg] or '', pattern) then return true end end return false 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 oddEven = cfg.marker.oddeven local data = args['list'..num] hasChild = false if data:sub(1, 12) == '</div><table' then hasChild = true oddEven = cfg.marker.contains_subgroup end local row = mw.html.create('tr'):addClass('navbox-row'..oddEven) 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') -- Only add hlist class if listclass parameter does not contain hlist or plainlist if not (args.listclass and (hasListClass(cfg.pattern.hlist, 'listclass') or hasListClass(cfg.pattern.plainlist, 'listclass'))) then hlistcell:addClass('hlist') end hlistcell:wikitext( processItem(data) ) 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 if hasChild then listcell:cssText('background: none') end if args['group'..num] then local groupcell = mw.html.create('th'):addClass('navbox-group'):attr('scope','row'):wikitext( args['group'..num] ) if args.groupclass then groupcell:addClass( args.groupclass ) end if args.groupstyle then groupcell:cssText( args.groupstyle ) end row:node( groupcell ) else listcell:attr('colspan',2) if not hasChild then listcell:addClass('no-group') end 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 or hasTitle or args.above 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 -- Create the root HTML element if isChild then navbox = mw.html.create('table'):addClass('navbox-subgroup') else navbox = mw.html.create('table'):addClass('navbox') end if not isChild or args.title then hasTitle = true end if hasTitle and isChild then navbox:addClass('navbox-subgroup-with-title') end if hasTitle and 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 if args.bodyclass then navbox:addClass(args.bodyclass) end if args.bodystyle then navbox:cssText(args.bodystyle) end -- Process... processTitle() processAboveBelow('above') processRows() processAboveBelow('below') if not isChild then return striped(tostring(navbox), border) else local wrapper = mw.html.create('') wrapper:wikitext('</div>') wrapper:node(navbox) wrapper:wikitext('<div class="hlist">') return striped(tostring(wrapper), border) 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) return _navbox(preProcessArgs(frame)) end return p
Summary:
Please note that all contributions to TBATE Wiki may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
TBATE Wiki:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Template used on this page:
Module:Navbox
(
edit
)
Navigation menu
Page actions
Page
Discussion
Read
Edit
Edit source
History
Page actions
Page
Discussion
More
Tools
Personal tools
Not logged in
Talk
Contributions
Create account
Log in
Navigation
Main page
Recent changes
Random page
Help about MediaWiki
Search
Tools
What links here
Related changes
Special pages
Page information