Module:Navbox
Tools
Actions
General
Sister projects
Print/export
![]() | This Lua module is used onapproximately 4,400,000 pages, or roughly 1496% of all pages. To avoid major disruption and server load, any changes should be tested in the module's/sandbox or/testcases subpages, or in your ownmodule sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on thetalk page before implementing them. |
![]() | This module issubject to page protection. It is ahighly visible module in use by a very large number of pages, or issubstituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it isprotected from editing. |
![]() | This module depends on the following other modules: |
![]() | This module usesTemplateStyles: |
This module implements the {{Navbox}} template. Please see thetemplate page for usage instructions.
localp={}localnavbar=require('Module:Navbar')._navbarlocalcfg=mw.loadData('Module:Navbox/configuration')localgetArgs-- lazily initializedlocalargslocalformat=string.formatlocalfunctionstriped(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.localorphanCat=cfg.category.orphanifborder==cfg.keyword.border_subgroupandargs[cfg.arg.orphan]~=cfg.keyword.orphan_yesthen-- No change; striping occurs in outermost navbox.returnwikitext..orphanCatendlocalfirst,second=cfg.class.navbox_odd_part,cfg.class.navbox_even_partifargs[cfg.arg.evenodd]thenifargs[cfg.arg.evenodd]==cfg.keyword.evenodd_swapthenfirst,second=second,firstelsefirst=args[cfg.arg.evenodd]second=firstendendlocalchangeriffirst==secondthenchanger=firstelselocalindex=0changer=function(code)ifcode=='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=0returnfirstendindex=index+1returnindex%2==1andfirstorsecondendendlocalregex=orphanCat:gsub('([%[%]])','%%%1')return(wikitext:gsub(regex,''):gsub(cfg.marker.regex,changer))-- () omits gsub countendlocalfunctionprocessItem(item,nowrapitems)ifitem: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'endifnowrapitems==cfg.keyword.nowrapitems_yesthenlocallines={}forlinein(item..'\n'):gmatch('([^\n]*)\n')dolocalprefix,content=line:match('^([*:;#]+)%s*(.*)')ifprefixandnotcontent:match(cfg.pattern.nowrap)thenline=format(cfg.nowrap_item,prefix,content)endtable.insert(lines,line)enditem=table.concat(lines,'\n')endifitem:match('^[*:;#]')thenreturn'\n'..item..'\n'endreturnitemendlocalfunctionhas_navbar()returnargs[cfg.arg.navbar]~=cfg.keyword.navbar_offandargs[cfg.arg.navbar]~=cfg.keyword.navbar_plainand(args[cfg.arg.name]ormw.getCurrentFrame():getParent():getTitle():gsub(cfg.pattern.sandbox,'')~=cfg.pattern.navbox)endlocalfunctionrenderNavBar(titleCell)ifhas_navbar()thentitleCell:wikitext(navbar{[cfg.navbar.name]=args[cfg.arg.name],[cfg.navbar.mini]=1,[cfg.navbar.fontstyle]=(args[cfg.arg.basestyle]or'')..';'..(args[cfg.arg.titlestyle]or'')..';background:none transparent;border:none;box-shadow:none;padding:0;'})endendlocalfunctionrenderTitleRow(tbl)ifnotargs[cfg.arg.title]thenreturnendlocaltitleRow=tbl:tag('tr')localtitleCell=titleRow:tag('th'):attr('scope','col')localtitleColspan=2ifargs[cfg.arg.imageleft]thentitleColspan=titleColspan+1endifargs[cfg.arg.image]thentitleColspan=titleColspan+1endtitleCell:cssText(args[cfg.arg.basestyle]):cssText(args[cfg.arg.titlestyle]):addClass(cfg.class.navbox_title):attr('colspan',titleColspan)renderNavBar(titleCell)titleCell:tag('div')-- id for aria-labelledby attribute:attr('id',mw.uri.anchorEncode(args[cfg.arg.title])):addClass(args[cfg.arg.titleclass]):css('font-size','114%'):css('margin','0 4em'):wikitext(processItem(args[cfg.arg.title]))endlocalfunctiongetAboveBelowColspan()localret=2ifargs[cfg.arg.imageleft]thenret=ret+1endifargs[cfg.arg.image]thenret=ret+1endreturnretendlocalfunctionrenderAboveRow(tbl)ifnotargs[cfg.arg.above]thenreturnendtbl:tag('tr'):tag('td'):addClass(cfg.class.navbox_abovebelow):addClass(args[cfg.arg.aboveclass]):cssText(args[cfg.arg.basestyle]):cssText(args[cfg.arg.abovestyle]):attr('colspan',getAboveBelowColspan()):tag('div')-- id for aria-labelledby attribute, if no title:attr('id',args[cfg.arg.title]andnilormw.uri.anchorEncode(args[cfg.arg.above])):wikitext(processItem(args[cfg.arg.above],args[cfg.arg.nowrapitems]))endlocalfunctionrenderBelowRow(tbl)ifnotargs[cfg.arg.below]thenreturnendtbl:tag('tr'):tag('td'):addClass(cfg.class.navbox_abovebelow):addClass(args[cfg.arg.belowclass]):cssText(args[cfg.arg.basestyle]):cssText(args[cfg.arg.belowstyle]):attr('colspan',getAboveBelowColspan()):tag('div'):wikitext(processItem(args[cfg.arg.below],args[cfg.arg.nowrapitems]))endlocalfunctionrenderListRow(tbl,index,listnum,listnums_size)localrow=tbl:tag('tr')ifindex==1andargs[cfg.arg.imageleft]thenrow:tag('td'):addClass(cfg.class.noviewer):addClass(cfg.class.navbox_image):addClass(args[cfg.arg.imageclass]):css('width','1px')-- Minimize width:css('padding','0 2px 0 0'):cssText(args[cfg.arg.imageleftstyle]):attr('rowspan',listnums_size):tag('div'):wikitext(processItem(args[cfg.arg.imageleft]))endlocalgroup_and_num=format(cfg.arg.group_and_num,listnum)localgroupstyle_and_num=format(cfg.arg.groupstyle_and_num,listnum)ifargs[group_and_num]thenlocalgroupCell=row:tag('th')-- id for aria-labelledby attribute, if lone group with no title or aboveiflistnum==1andnot(args[cfg.arg.title]orargs[cfg.arg.above]orargs[cfg.arg.group2])thengroupCell:attr('id',mw.uri.anchorEncode(args[cfg.arg.group1]))endgroupCell:attr('scope','row'):addClass(cfg.class.navbox_group):addClass(args[cfg.arg.groupclass]):cssText(args[cfg.arg.basestyle])-- If groupwidth not specified, minimize width:css('width',args[cfg.arg.groupwidth]or'1%')groupCell:cssText(args[cfg.arg.groupstyle]):cssText(args[groupstyle_and_num]):wikitext(args[group_and_num])endlocallistCell=row:tag('td')ifargs[group_and_num]thenlistCell:addClass(cfg.class.navbox_list_with_group)elselistCell:attr('colspan',2)endifnotargs[cfg.arg.groupwidth]thenlistCell:css('width','100%')endlocalrowstyle-- usually nil so cssText(rowstyle) usually adds nothingifindex%2==1thenrowstyle=args[cfg.arg.oddstyle]elserowstyle=args[cfg.arg.evenstyle]endlocallist_and_num=format(cfg.arg.list_and_num,listnum)locallistText=args[list_and_num]localoddEven=cfg.marker.oddeveniflistText:sub(1,12)=='</div><table'then-- Assume list text is for a subgroup navbox so no automatic striping for this row.oddEven=listText:find(cfg.pattern.navbox_title)andcfg.marker.restartorcfg.class.navbox_odd_partendlocalliststyle_and_num=format(cfg.arg.liststyle_and_num,listnum)locallistclass_and_num=format(cfg.arg.listclass_and_num,listnum)listCell:css('padding','0'):cssText(args[cfg.arg.liststyle]):cssText(rowstyle):cssText(args[liststyle_and_num]):addClass(cfg.class.navbox_list):addClass(cfg.class.navbox_part..oddEven):addClass(args[cfg.arg.listclass]):addClass(args[listclass_and_num]):tag('div'):css('padding',(index==1andargs[cfg.arg.list1padding])orargs[cfg.arg.listpadding]or'0 0.25em'):wikitext(processItem(listText,args[cfg.arg.nowrapitems]))ifindex==1andargs[cfg.arg.image]thenrow:tag('td'):addClass(cfg.class.noviewer):addClass(cfg.class.navbox_image):addClass(args[cfg.arg.imageclass]):css('width','1px')-- Minimize width:css('padding','0 0 0 2px'):cssText(args[cfg.arg.imagestyle]):attr('rowspan',listnums_size):tag('div'):wikitext(processItem(args[cfg.arg.image]))endendlocalfunctionhas_list_class(htmlclass)localpatterns={'^'..htmlclass..'$','%s'..htmlclass..'$','^'..htmlclass..'%s','%s'..htmlclass..'%s'}forarg,_inpairs(args)doiftype(arg)=='string'andmw.ustring.find(arg,cfg.pattern.class)thenfor_,patterninipairs(patterns)doifmw.ustring.find(args[arg]or'',pattern)thenreturntrueendendendendreturnfalseend-- there are a lot of list classes in the wild, so we add their TemplateStyleslocalfunctionadd_list_styles()localframe=mw.getCurrentFrame()localfunctionadd_list_templatestyles(htmlclass,templatestyles)ifhas_list_class(htmlclass)thenreturnframe:extensionTag{name='templatestyles',args={src=templatestyles}}elsereturn''endendlocalhlist_styles=add_list_templatestyles('hlist',cfg.hlist_templatestyles)localplainlist_styles=add_list_templatestyles('plainlist',cfg.plainlist_templatestyles)-- a second workaround for [[phab:T303378]]-- when that issue is fixed, we can actually use has_navbar not to emit the-- tag here if we wantifhas_navbar()andhlist_styles==''thenhlist_styles=frame:extensionTag{name='templatestyles',args={src=cfg.hlist_templatestyles}}end-- hlist -> plainlist is best-effort to preserve old Common.css ordering.-- this ordering is not a guarantee because most navboxes will emit only-- one of these classes [hlist_note]returnhlist_styles..plainlist_stylesendlocalfunctionneedsHorizontalLists(border)ifborder==cfg.keyword.border_subgrouporargs[cfg.arg.tracking]==cfg.keyword.tracking_nothenreturnfalseendreturnnothas_list_class(cfg.pattern.hlist)andnothas_list_class(cfg.pattern.plainlist)endlocalfunctionhasBackgroundColors()for_,keyinipairs({cfg.arg.titlestyle,cfg.arg.groupstyle,cfg.arg.basestyle,cfg.arg.abovestyle,cfg.arg.belowstyle})doiftostring(args[key]):find('background',1,true)thenreturntrueendendreturnfalseendlocalfunctionhasBorders()for_,keyinipairs({cfg.arg.groupstyle,cfg.arg.basestyle,cfg.arg.abovestyle,cfg.arg.belowstyle})doiftostring(args[key]):find('border',1,true)thenreturntrueendendreturnfalseendlocalfunctionisIllegible()localstyleratio=require('Module:Color contrast')._styleratioforkey,styleinpairs(args)doiftostring(key):match(cfg.pattern.style)thenifstyleratio{mw.text.unstripNoWiki(style)}<4.5thenreturntrueendendendreturnfalseendlocalfunctiongetTrackingCategories(border)localcats={}ifneedsHorizontalLists(border)thentable.insert(cats,cfg.category.horizontal_lists)endifhasBackgroundColors()thentable.insert(cats,cfg.category.background_colors)endifisIllegible()thentable.insert(cats,cfg.category.illegible)endifhasBorders()thentable.insert(cats,cfg.category.borders)endreturncatsendlocalfunctionrenderTrackingCategories(builder,border)localtitle=mw.title.getCurrentTitle()iftitle.namespace~=10thenreturnend-- not in template spacelocalsubpage=title.subpageTextifsubpage==cfg.keyword.subpage_docorsubpage==cfg.keyword.subpage_sandboxorsubpage==cfg.keyword.subpage_testcasesthenreturnendfor_,catinipairs(getTrackingCategories(border))dobuilder:wikitext('[[Category:'..cat..']]')endendlocalfunctionrenderMainTable(border,listnums)localtbl=mw.html.create('table'):addClass(cfg.class.nowraplinks):addClass(args[cfg.arg.bodyclass])localstate=args[cfg.arg.state]ifargs[cfg.arg.title]andstate~=cfg.keyword.state_plainandstate~=cfg.keyword.state_offthenifstate==cfg.keyword.state_collapsedthenstate=cfg.class.collapsedendtbl:addClass(cfg.class.collapsible):addClass(stateorcfg.class.autocollapse)endtbl:css('border-spacing',0)ifborder==cfg.keyword.border_subgrouporborder==cfg.keyword.border_nonethentbl:addClass(cfg.class.navbox_subgroup):cssText(args[cfg.arg.bodystyle]):cssText(args[cfg.arg.style])else-- regular navbox - bodystyle and style will be applied to the wrapper tabletbl:addClass(cfg.class.navbox_inner):css('background','transparent'):css('color','inherit')endtbl:cssText(args[cfg.arg.innerstyle])renderTitleRow(tbl)renderAboveRow(tbl)locallistnums_size=#listnumsfori,listnuminipairs(listnums)dorenderListRow(tbl,i,listnum,listnums_size)endrenderBelowRow(tbl)returntblendlocalfunctionadd_navbox_styles(hiding_templatestyles)localframe=mw.getCurrentFrame()-- This is a lambda so that it doesn't need the frame as a parameterlocalfunctionadd_user_styles(templatestyles)iftemplatestylesandtemplatestyles~=''thenreturnframe:extensionTag{name='templatestyles',args={src=templatestyles}}endreturn''end-- get templatestyles. load base from config so that Lua only needs to do-- the work once of parser tag expansionlocalbase_templatestyles=cfg.templatestyleslocaltemplatestyles=add_user_styles(args[cfg.arg.templatestyles])localchild_templatestyles=add_user_styles(args[cfg.arg.child_templatestyles])-- The 'navbox-styles' div exists to wrap the styles to work around T200206-- more elegantly. Instead of combinatorial rules, this ends up being linear-- number of CSS rules.returnmw.html.create('div'):addClass(cfg.class.navbox_styles):wikitext(add_list_styles()..-- see [hlist_note] applied to 'before base_templatestyles'base_templatestyles..templatestyles..child_templatestyles..table.concat(hiding_templatestyles)):done()end-- work around [[phab:T303378]]-- for each arg: find all the templatestyles strip markers, insert them into a-- table. then remove all templatestyles markers from the arglocalfunctionmove_hiding_templatestyles(args)localgfind=string.gfindlocalgsub=string.gsublocaltemplatestyles_markers={}localstrip_marker_pattern='(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)'fork,arginpairs(args)doformarkeringfind(arg,strip_marker_pattern)dotable.insert(templatestyles_markers,marker)endargs[k]=gsub(arg,strip_marker_pattern,'')endreturntemplatestyles_markersendfunctionp._navbox(navboxArgs)args=navboxArgslocalhiding_templatestyles=move_hiding_templatestyles(args)locallistnums={}fork,_inpairs(args)doiftype(k)=='string'thenlocallistnum=k:match(cfg.pattern.listnum)iflistnumthentable.insert(listnums,tonumber(listnum))endendendtable.sort(listnums)localborder=mw.text.trim(args[cfg.arg.border]orargs[1]or'')ifborder==cfg.keyword.border_childthenborder=cfg.keyword.border_subgroupend-- render the main body of the navboxlocaltbl=renderMainTable(border,listnums)localres=mw.html.create()-- render the appropriate wrapper for the navbox, based on the border paramifborder==cfg.keyword.border_nonethenres:node(add_navbox_styles(hiding_templatestyles))localnav=res:tag('div'):attr('role','navigation'):node(tbl)-- aria-labelledby title, otherwise above, otherwise lone groupifargs[cfg.arg.title]orargs[cfg.arg.above]or(args[cfg.arg.group1]andnotargs[cfg.arg.group2])thennav:attr('aria-labelledby',mw.uri.anchorEncode(args[cfg.arg.title]orargs[cfg.arg.above]orargs[cfg.arg.group1]))elsenav:attr('aria-label',cfg.aria_label)endelseifborder==cfg.keyword.border_subgroupthen-- 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>')elseres:node(add_navbox_styles(hiding_templatestyles))localnav=res:tag('div'):attr('role','navigation'):addClass(cfg.class.navbox):addClass(args[cfg.arg.navboxclass]):cssText(args[cfg.arg.bodystyle]):cssText(args[cfg.arg.style]):css('padding','3px'):node(tbl)-- aria-labelledby title, otherwise above, otherwise lone groupifargs[cfg.arg.title]orargs[cfg.arg.above]or(args[cfg.arg.group1]andnotargs[cfg.arg.group2])thennav:attr('aria-labelledby',mw.uri.anchorEncode(args[cfg.arg.title]orargs[cfg.arg.above]orargs[cfg.arg.group1]))elsenav:attr('aria-label',cfg.aria_label)endendif(args[cfg.arg.nocat]orcfg.keyword.nocat_false):lower()==cfg.keyword.nocat_falsethenrenderTrackingCategories(res,border)endreturnstriped(tostring(res),border)endfunctionp.navbox(frame)ifnotgetArgsthengetArgs=require('Module:Arguments').getArgsendargs=getArgs(frame,{wrappers={cfg.pattern.navbox}})-- Read the arguments in the order they'll be output in, to make references-- number in the right order.local__=args[cfg.arg.title]_=args[cfg.arg.above]-- Limit this to 20 as covering 'most' cases (that's a SWAG) and because-- iterator approach won't work herefori=1,20do_=args[format(cfg.arg.group_and_num,i)]_=args[format(cfg.arg.list_and_num,i)]end_=args[cfg.arg.below]returnp._navbox(args)endreturnp